"""
Configuration of trspecfit plotting functions
"""
import copy as cp
from dataclasses import dataclass, fields
# Plot configuration hierarchy:
#
# Project (defaults from YAML)
# ↓
# File (can customize persistently per file)
# ↓
# Model (inherits from File, no customization)
# ↓
# plot call (can override temporarily for one plot)
[docs]
@dataclass
class PlotConfig:
"""
Configuration for plot appearance and behavior.
This class bundles all plot-related settings that can be passed to
plotting functions. It can be created standalone or from a Project instance.
Attributes
----------
x_label : str
X-axis label (energy for 2D, energy or time for 1D)
y_label : str
Y-axis label (time for 2D, intensity for 1D)
z_label : str
Z-axis/colorbar label (intensity for 2D plots)
title : str
Plot title
x_dir : str
X-axis direction: 'def' (default) or 'rev' (reversed)
x_type : str
X-axis scale: 'lin' (linear) or 'log' (logarithmic)
y_dir : str
Y-axis direction: 'def' (default) or 'rev' (reversed)
y_type : str
Y-axis scale: 'lin' (linear) or 'log' (logarithmic)
x_lim : tuple[float, float] | None
X-axis limits (min, max)
y_lim : tuple[float, float] | None
Y-axis limits (min, max)
z_type : str
Color scale type: 'lin' (linear) or 'log' (logarithmic)
z_lim : tuple[float, float] | None
Color scale limits for 2D plots (min, max)
dpi_plot : int
DPI for displaying plots
dpi_save : int
DPI for saving plots
z_colormap : str
Colormap name for 2D plots
data_slice : list[list[int]] | None
Data slicing indices for 2D plots: [[x_start, x_stop], [y_start, y_stop]]
colors : list[str] | None
List of colors for line plots
linestyles : list[str] | None
List of line styles
linewidths : list[float] | None
List of line widths
markers : list[str] | None
List of marker styles
markersizes : list[float] | None
List of marker sizes
alphas : list[float] | None
List of opacity values (0–1) for each trace
legend : list[str] | None
List of legend labels
waterfall : float
Y-offset between plots for waterfall display
vlines : list[float] | None
X-coordinates for vertical lines
hlines : list[float] | None
Y-coordinates for horizontal lines
ticksize : float | None
Font size for tick labels
Examples
--------
Create a configuration with custom settings:
>>> config = PlotConfig(x_label='Energy (eV)', x_dir='rev', dpi_plot=150)
>>> plot_1d(data, x, config=config)
Create from Project settings:
>>> project = Project(path='...', config_file='project.yaml')
>>> config = PlotConfig.from_project(project)
>>> plot_1d(data, x, config=config)
Override Project settings:
>>> config = PlotConfig.from_project(project, x_label='Binding Energy (eV)')
>>> plot_2d(data, x, y, config=config)
Override config for a specific plot:
>>> config = PlotConfig.from_project(project)
>>> plot_1d(data, x, config=config, x_dir='rev', linewidth=2)
Create multiple configs from one project:
>>> project = Project(path='...')
>>> default_config = PlotConfig.from_project(project)
>>> pub_config = PlotConfig.from_project(project, dpi_save=600)
>>> talk_config = PlotConfig.from_project(project, dpi_plot=150)
"""
# Axis labels
x_label: str = "x axis"
y_label: str = "y axis"
z_label: str = "z axis"
title: str = ""
# Axis behavior
x_dir: str = "def"
x_type: str = "lin"
y_dir: str = "def"
y_type: str = "lin"
# Axis limits
x_lim: tuple[float, float] | None = None
y_lim: tuple[float, float] | None = None
# Display settings
dpi_plot: int = 100
dpi_save: int = 300
# Residual multiplier for 1D fit plots
res_mult: float = 5
# 2D plot settings
z_colormap: str = "viridis"
z_colorbar: str = "ver" # 'ver' or 'hor'
z_type: str = "lin" # 'lin' or 'log' for color scale
# 2D data handling
data_slice: list[list[int]] | None = None
z_lim: tuple[float, float] | None = None
# Line plot styles
colors: list[str] | None = None
linestyles: list[str] | None = None
linewidths: list[float] | None = None
markers: list[str] | None = None
markersizes: list[float] | None = None
alphas: list[float] | None = None
# Plot annotations
legend: list[str] | None = None
waterfall: float = 0
vlines: list[float] | None = None
hlines: list[float] | None = None
ticksize: float | None = None
# Normalization
y_norm: int = 0 # 0: no normalization, 1: normalize to [0,1]
y_scale: list[float] | None = None
[docs]
@classmethod
def from_project(cls, project, **overrides) -> "PlotConfig":
"""
Create PlotConfig from Project settings.
Parameters
----------
project : Project
Project instance with plot settings
**overrides : dict
Any parameters to override from project defaults
Returns
-------
PlotConfig
Configuration object with settings from project
"""
project_aliases = {
"x_label": "e_label",
"y_label": "t_label",
"dpi_plot": "dpi_plt",
}
limit_fields = {"x_lim", "y_lim", "z_lim"}
config_dict = {}
for field in fields(cls):
source_attr = project_aliases.get(field.name, field.name)
if not hasattr(project, source_attr):
continue
value = cp.deepcopy(getattr(project, source_attr))
if field.name in limit_fields and value is not None:
value = tuple(value)
config_dict[field.name] = value
# Apply any overrides
config_dict.update(overrides)
return cls(**config_dict)
#
[docs]
def update(self, **kwargs) -> "PlotConfig":
"""
Update configuration attributes.
Parameters
----------
**kwargs : dict
Attributes to update
Returns
-------
PlotConfig
Self (for chaining)
"""
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
else:
raise AttributeError(f"PlotConfig has no attribute '{key}'")
return self
#
[docs]
def copy(self, **overrides) -> "PlotConfig":
"""
Create a copy of this config with optional overrides.
Parameters
----------
**overrides : dict
Attributes to override in the copy
Returns
-------
PlotConfig
New configuration object
"""
new_config = cp.copy(self)
new_config.update(**overrides)
return new_config