batplot 1.8.41__tar.gz → 1.8.42__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {batplot-1.8.41/batplot.egg-info → batplot-1.8.42}/PKG-INFO +1 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot/__init__.py +1 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot/color_utils.py +72 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/data/CHANGELOG.md +4 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/ec_common.py +7 -4
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/routing.py +4 -4
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/actions.py +10 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/dqdv_2d.py +5 -2
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/interactive.py +23 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/style.py +4 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/actions.py +6 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/style.py +1 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/cif.py +2 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/colors.py +3 -24
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/interactive.py +54 -14
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/style.py +118 -135
- {batplot-1.8.41 → batplot-1.8.42}/batplot/session.py +61 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/version_check.py +1 -2
- {batplot-1.8.41 → batplot-1.8.42/batplot.egg-info}/PKG-INFO +1 -1
- {batplot-1.8.41 → batplot-1.8.42}/batplot.egg-info/SOURCES.txt +1 -0
- {batplot-1.8.41 → batplot-1.8.42}/pyproject.toml +1 -1
- batplot-1.8.42/tests/test_color_utils.py +31 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_contracts.py +10 -13
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_xy_modules.py +152 -2
- {batplot-1.8.41 → batplot-1.8.42}/LICENSE +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/MANIFEST.in +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/NOTICE +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/README.md +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/args.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/batch.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/batplot.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/canvas_interactive.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/cif.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/cli.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/config.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/converters.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/dev_upgrade.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/modes.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/axis_state.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/files.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/fonts.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/interactive_state.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/menu_rendering.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/menus.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/palettes.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/smoothing.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/sources.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/spines.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/terminal.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/common/title_offsets.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/actions.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/colors.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/interactive.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/labels.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/legend.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/menu.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/session.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/cpc/snapshots.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/colors.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/export.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/labels.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/legend.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/legend_order.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/line_style.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/menu.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/routing.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/session.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/electrochem/spine_colors.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/colors.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/grid.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/interactive.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/ions_axis.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/labels.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/layout.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/line_style.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/menu.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/peaks.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/plot.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/routing.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/session.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/operando/visibility.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/session_routing.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/__init__.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/actions.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/arrange.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/axis_range.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/data_ops.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/derivative.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/game.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/labels.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/line_style.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/menu.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/peaks.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/pipeline.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/session.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plot_modes/xy/smoothing.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/plotting.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/readers.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/showcol.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/style.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/ui.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot/utils.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/batplot.egg-info/top_level.txt +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/setup.cfg +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/setup.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_cli_smoke.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_common_files.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_common_palettes.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_cpc_roundtrip.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_csv_readers.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_dev_upgrade.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_ec_roundtrip.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_interactive_menu_smoke.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_interactive_state.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_operando_roundtrip.py +0 -0
- {batplot-1.8.41 → batplot-1.8.42}/tests/test_xy_roundtrip.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.42
|
|
4
4
|
Summary: Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing
|
|
5
5
|
Author-email: Tian Dai <tianda@uio.no>
|
|
6
6
|
License: MIT License
|
|
@@ -177,6 +177,76 @@ def ensure_colormap(name: Optional[str]) -> bool:
|
|
|
177
177
|
return False
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
def get_colormap(name: Optional[str]) -> Optional[Colormap]:
|
|
181
|
+
"""Return a Colormap by name across matplotlib versions and custom maps.
|
|
182
|
+
|
|
183
|
+
Prefer this over ``matplotlib.cm.get_cmap`` — that API was removed in
|
|
184
|
+
matplotlib 3.11, which broke palette commands such as ``all viridis`` on
|
|
185
|
+
Windows installs with newer matplotlib.
|
|
186
|
+
"""
|
|
187
|
+
if not name:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
ensure_colormap(name)
|
|
191
|
+
|
|
192
|
+
candidates: List[str] = []
|
|
193
|
+
for candidate in (name, name.lower()):
|
|
194
|
+
if candidate and candidate not in candidates:
|
|
195
|
+
candidates.append(candidate)
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
from matplotlib import colormaps as mpl_colormaps
|
|
199
|
+
|
|
200
|
+
registry_get = getattr(mpl_colormaps, "get_cmap", None)
|
|
201
|
+
if callable(registry_get):
|
|
202
|
+
for candidate in candidates:
|
|
203
|
+
try:
|
|
204
|
+
return registry_get(candidate)
|
|
205
|
+
except Exception:
|
|
206
|
+
pass
|
|
207
|
+
for candidate in candidates:
|
|
208
|
+
try:
|
|
209
|
+
return mpl_colormaps[candidate]
|
|
210
|
+
except Exception:
|
|
211
|
+
pass
|
|
212
|
+
except Exception:
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
for candidate in candidates:
|
|
216
|
+
try:
|
|
217
|
+
return plt.get_cmap(candidate)
|
|
218
|
+
except Exception:
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
reversed_flag = name.lower().endswith("_r")
|
|
222
|
+
base = name[:-2] if reversed_flag else name
|
|
223
|
+
base_lower = base.lower()
|
|
224
|
+
|
|
225
|
+
custom = _CUSTOM_CMAPS.get(base_lower)
|
|
226
|
+
if custom:
|
|
227
|
+
try:
|
|
228
|
+
cmap_obj = LinearSegmentedColormap.from_list(base_lower, custom, N=256)
|
|
229
|
+
if reversed_flag:
|
|
230
|
+
cmap_obj = cmap_obj.reversed()
|
|
231
|
+
return cmap_obj
|
|
232
|
+
except Exception:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
if base_lower.startswith("batlow"):
|
|
236
|
+
try:
|
|
237
|
+
import cmcrameri.cm as cmc # type: ignore[import]
|
|
238
|
+
|
|
239
|
+
cmap_obj = getattr(cmc, base_lower, None) or getattr(cmc, "batlow", None)
|
|
240
|
+
if cmap_obj is not None:
|
|
241
|
+
if reversed_flag and hasattr(cmap_obj, "reversed"):
|
|
242
|
+
return cmap_obj.reversed()
|
|
243
|
+
return cmap_obj
|
|
244
|
+
except Exception:
|
|
245
|
+
pass
|
|
246
|
+
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
|
|
180
250
|
def _ansi_color_block_from_rgba(rgba) -> str:
|
|
181
251
|
"""Return a two-space block with the given RGBA color."""
|
|
182
252
|
try:
|
|
@@ -570,6 +640,8 @@ __all__ = [
|
|
|
570
640
|
'clear_user_colors',
|
|
571
641
|
'color_bar',
|
|
572
642
|
'color_block',
|
|
643
|
+
'ensure_colormap',
|
|
644
|
+
'get_colormap',
|
|
573
645
|
'manage_user_colors',
|
|
574
646
|
'palette_preview',
|
|
575
647
|
'print_user_colors',
|
|
@@ -20,7 +20,8 @@ except ImportError:
|
|
|
20
20
|
|
|
21
21
|
_EC_DEFAULT_FIGSIZE = (10.0, 6.0)
|
|
22
22
|
_EC_DEFAULT_LAYOUT = {'left': 0.12, 'right': 0.95, 'top': 0.88, 'bottom': 0.15}
|
|
23
|
-
|
|
23
|
+
# CPC shares the same canvas and plot-frame defaults as GC/CV/dQ/dV.
|
|
24
|
+
_CPC_DEFAULT_LAYOUT = _EC_DEFAULT_LAYOUT
|
|
24
25
|
_EC_DEFAULT_FRAME_SIZE = (
|
|
25
26
|
_EC_DEFAULT_FIGSIZE[0] * (_EC_DEFAULT_LAYOUT['right'] - _EC_DEFAULT_LAYOUT['left']),
|
|
26
27
|
_EC_DEFAULT_FIGSIZE[1] * (_EC_DEFAULT_LAYOUT['top'] - _EC_DEFAULT_LAYOUT['bottom']),
|
|
@@ -39,12 +40,14 @@ def _default_ec_figsize() -> tuple[float, float]:
|
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def _default_cpc_figsize() -> tuple[float, float]:
|
|
42
|
-
|
|
43
|
+
"""Alias for :func:`_default_ec_figsize` (GC/CV/dQ/dV/CPC share one default)."""
|
|
44
|
+
return _default_ec_figsize()
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
def _apply_default_ec_layout(fig, *, cpc: bool = False) -> None:
|
|
46
|
-
"""Apply the default layout
|
|
47
|
-
|
|
48
|
+
"""Apply the default electrochem layout (same for GC, CV, dQ/dV, and CPC)."""
|
|
49
|
+
del cpc # kept for backward compatibility; CPC uses the same layout as other EC modes
|
|
50
|
+
fig.subplots_adjust(**_EC_DEFAULT_LAYOUT)
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
def _resolve_mass(mass_arg, file_idx: int = 0):
|
|
@@ -23,7 +23,7 @@ import matplotlib.colors as mcolors # type: ignore[import-untyped]
|
|
|
23
23
|
|
|
24
24
|
from ...ec_common import (
|
|
25
25
|
_resolve_mass,
|
|
26
|
-
|
|
26
|
+
_default_ec_figsize,
|
|
27
27
|
_apply_default_ec_layout,
|
|
28
28
|
)
|
|
29
29
|
from ...batch import _apply_ec_style
|
|
@@ -340,8 +340,8 @@ def handle_cpc_mode(args) -> int:
|
|
|
340
340
|
print("No valid CPC data files to plot.")
|
|
341
341
|
exit(1)
|
|
342
342
|
|
|
343
|
-
# Plot (same
|
|
344
|
-
fig, ax = plt.subplots(figsize=
|
|
343
|
+
# Plot (same canvas and frame size as GC/CV/dQ/dV)
|
|
344
|
+
fig, ax = plt.subplots(figsize=_default_ec_figsize())
|
|
345
345
|
ax.set_xlabel('Cycle number', labelpad=8.0)
|
|
346
346
|
if is_epc:
|
|
347
347
|
ax.set_ylabel(r'Specific Energy (mWh g$^{-1}$)', labelpad=8.0)
|
|
@@ -439,7 +439,7 @@ def handle_cpc_mode(args) -> int:
|
|
|
439
439
|
print(f"Warning: Could not create CPC legend: {e}")
|
|
440
440
|
|
|
441
441
|
# Adjust layout to ensure top and bottom labels/titles are visible
|
|
442
|
-
_apply_default_ec_layout(fig
|
|
442
|
+
_apply_default_ec_layout(fig)
|
|
443
443
|
|
|
444
444
|
# Check for style file in file list
|
|
445
445
|
style_file_path = None
|
|
@@ -540,6 +540,16 @@ def handle_import_style_command(ctx: ElectrochemActionContext) -> None: # pyrig
|
|
|
540
540
|
plt.rcParams['mathtext.fontset'] = font_cfg['mathtext_fontset']
|
|
541
541
|
except Exception:
|
|
542
542
|
pass
|
|
543
|
+
axis_label_colors = cfg.get('axis_label_colors') or {}
|
|
544
|
+
try:
|
|
545
|
+
if axis_label_colors.get('x'):
|
|
546
|
+
ax.xaxis.label.set_color(axis_label_colors['x'])
|
|
547
|
+
ax._stored_xlabel_color = axis_label_colors['x']
|
|
548
|
+
if axis_label_colors.get('y'):
|
|
549
|
+
ax.yaxis.label.set_color(axis_label_colors['y'])
|
|
550
|
+
ax._stored_ylabel_color = axis_label_colors['y']
|
|
551
|
+
except Exception:
|
|
552
|
+
pass
|
|
543
553
|
except Exception as e:
|
|
544
554
|
print(f"Warning: Could not apply figure/font settings: {e}")
|
|
545
555
|
|
|
@@ -10,6 +10,7 @@ from matplotlib.ticker import FuncFormatter, NullFormatter # type: ignore[impor
|
|
|
10
10
|
|
|
11
11
|
from ...ui import capture_axes_tick_locators, restore_axes_tick_locators
|
|
12
12
|
from ...utils import natural_sort_key
|
|
13
|
+
from ...ec_common import _default_ec_figsize
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def _dqdv_2d_row_tick_indices(n_rows: int, max_ticks: int = 24) -> np.ndarray:
|
|
@@ -536,11 +537,13 @@ def restore_dqdv_2d_companion_figure(blob: Dict[str, Any]) -> Optional[Tuple[Any
|
|
|
536
537
|
row_labels = [str(x) for x in (blob.get("row_labels") or [])]
|
|
537
538
|
zlab = str(blob.get("zlabel") or "dQ/dV")
|
|
538
539
|
cmap = str(blob.get("cmap") or "viridis")
|
|
539
|
-
figsize = blob.get("figsize")
|
|
540
|
+
figsize = blob.get("figsize")
|
|
541
|
+
if not figsize:
|
|
542
|
+
figsize = list(_default_ec_figsize())
|
|
540
543
|
try:
|
|
541
544
|
cfig, cax = plt.subplots(figsize=(float(figsize[0]), float(figsize[1])))
|
|
542
545
|
except Exception:
|
|
543
|
-
cfig, cax = plt.subplots(figsize=(
|
|
546
|
+
cfig, cax = plt.subplots(figsize=_default_ec_figsize())
|
|
544
547
|
Zm = np.ma.masked_invalid(Z)
|
|
545
548
|
extent = (0.0, float(2 * dv), -0.5, float(Zm.shape[0] - 0.5))
|
|
546
549
|
im = cax.imshow(
|
|
@@ -32,6 +32,7 @@ from matplotlib.ticker import ( # type: ignore[import-untyped]
|
|
|
32
32
|
AutoLocator,
|
|
33
33
|
)
|
|
34
34
|
from ...plotting import update_labels as _update_labels
|
|
35
|
+
from ...ec_common import _default_ec_figsize
|
|
35
36
|
import matplotlib as mpl # type: ignore[import-untyped]
|
|
36
37
|
from ...color_utils import (
|
|
37
38
|
color_block,
|
|
@@ -981,6 +982,11 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
|
|
|
981
982
|
'font_size': plt.rcParams.get('font.size'),
|
|
982
983
|
'font_family': plt.rcParams.get('font.family'),
|
|
983
984
|
'font_sans_serif': list(plt.rcParams.get('font.sans-serif', [])),
|
|
985
|
+
'mathtext_fontset': plt.rcParams.get('mathtext.fontset'),
|
|
986
|
+
'axis_label_colors': {
|
|
987
|
+
'x': getattr(ax, '_stored_xlabel_color', None) or ax.xaxis.label.get_color(),
|
|
988
|
+
'y': getattr(ax, '_stored_ylabel_color', None) or ax.yaxis.label.get_color(),
|
|
989
|
+
},
|
|
984
990
|
'titles': {
|
|
985
991
|
'top_x': bool(getattr(ax, '_top_xlabel_on', False)),
|
|
986
992
|
'right_y': bool(getattr(ax, '_right_ylabel_on', False))
|
|
@@ -1210,6 +1216,22 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
|
|
|
1210
1216
|
_rebuild_legend(ax)
|
|
1211
1217
|
except Exception:
|
|
1212
1218
|
pass
|
|
1219
|
+
try:
|
|
1220
|
+
mathtext_fontset = snap.get('mathtext_fontset')
|
|
1221
|
+
if mathtext_fontset:
|
|
1222
|
+
mpl.rcParams['mathtext.fontset'] = mathtext_fontset
|
|
1223
|
+
except Exception:
|
|
1224
|
+
pass
|
|
1225
|
+
try:
|
|
1226
|
+
axis_label_colors = snap.get('axis_label_colors') or {}
|
|
1227
|
+
if axis_label_colors.get('x') is not None:
|
|
1228
|
+
ax.xaxis.label.set_color(axis_label_colors['x'])
|
|
1229
|
+
ax._stored_xlabel_color = axis_label_colors['x']
|
|
1230
|
+
if axis_label_colors.get('y') is not None:
|
|
1231
|
+
ax.yaxis.label.set_color(axis_label_colors['y'])
|
|
1232
|
+
ax._stored_ylabel_color = axis_label_colors['y']
|
|
1233
|
+
except Exception:
|
|
1234
|
+
pass
|
|
1213
1235
|
# Title offsets - all four titles
|
|
1214
1236
|
try:
|
|
1215
1237
|
offsets = snap.get('title_offsets', {})
|
|
@@ -1998,7 +2020,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
|
|
|
1998
2020
|
im = None
|
|
1999
2021
|
dv = float(v_hi - v_lo)
|
|
2000
2022
|
try:
|
|
2001
|
-
cfig, cax = plt.subplots(figsize=(
|
|
2023
|
+
cfig, cax = plt.subplots(figsize=_default_ec_figsize())
|
|
2002
2024
|
Zm = np.ma.masked_invalid(Z)
|
|
2003
2025
|
extent = (0.0, float(2 * dv), -0.5, float(Zm.shape[0] - 0.5))
|
|
2004
2026
|
im = cax.imshow(
|
|
@@ -321,6 +321,10 @@ def _get_style_snapshot(fig, ax, cycle_lines: Dict, tick_state: Dict, file_data:
|
|
|
321
321
|
'size': font_size,
|
|
322
322
|
'mathtext_fontset': plt.rcParams.get('mathtext.fontset'),
|
|
323
323
|
},
|
|
324
|
+
'axis_label_colors': {
|
|
325
|
+
'x': mcolors.to_hex(getattr(ax, '_stored_xlabel_color', None) or ax.xaxis.label.get_color()),
|
|
326
|
+
'y': mcolors.to_hex(getattr(ax, '_stored_ylabel_color', None) or ax.yaxis.label.get_color()),
|
|
327
|
+
},
|
|
324
328
|
'legend': {
|
|
325
329
|
'visible': legend_visible,
|
|
326
330
|
'position_inches': legend_xy_in,
|
|
@@ -855,6 +855,12 @@ def handle_import_style(ctx: OperandoActionContext) -> None: # pyright: ignore[
|
|
|
855
855
|
font = cfg.get('font', {})
|
|
856
856
|
fam = font.get('family')
|
|
857
857
|
size = font.get('size')
|
|
858
|
+
mathtext_fs = font.get('mathtext_fontset')
|
|
859
|
+
if mathtext_fs:
|
|
860
|
+
try:
|
|
861
|
+
plt.rcParams['mathtext.fontset'] = mathtext_fs
|
|
862
|
+
except Exception:
|
|
863
|
+
pass
|
|
858
864
|
if fam or size is not None:
|
|
859
865
|
try:
|
|
860
866
|
set_fonts(family=fam if fam else None, size=size if size is not None else None)
|
|
@@ -367,7 +367,7 @@ def build_operando_ec_style_config_v2(fig, ax, im, cbar, ec_ax, exp_choice: str)
|
|
|
367
367
|
"title_offsets": op_title_offsets,
|
|
368
368
|
},
|
|
369
369
|
"ec": ec_payload,
|
|
370
|
-
"font": {"family": fam, "size": fsize},
|
|
370
|
+
"font": {"family": fam, "size": fsize, "mathtext_fontset": plt.rcParams.get("mathtext.fontset")},
|
|
371
371
|
"colorbar": {"label": cb_label_text, "mode": cb_label_mode, "visible": cb_vis},
|
|
372
372
|
}
|
|
373
373
|
default_ext = ".bps" if exp_choice == "ps" else ".bpsg"
|
|
@@ -24,6 +24,7 @@ from ...utils import (
|
|
|
24
24
|
from ...color_utils import (
|
|
25
25
|
color_block,
|
|
26
26
|
ensure_colormap,
|
|
27
|
+
get_colormap,
|
|
27
28
|
resolve_color_token,
|
|
28
29
|
_CUSTOM_CMAPS,
|
|
29
30
|
)
|
|
@@ -352,7 +353,7 @@ def run_cif_ticks_menu(
|
|
|
352
353
|
print("No valid indices parsed.")
|
|
353
354
|
continue
|
|
354
355
|
try:
|
|
355
|
-
cmap =
|
|
356
|
+
cmap = get_colormap(palette_name)
|
|
356
357
|
except Exception:
|
|
357
358
|
cmap = None
|
|
358
359
|
if cmap is None:
|
|
@@ -9,13 +9,10 @@ so undo and CIF redraw behavior are unchanged.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import importlib as _il
|
|
13
12
|
from typing import Any, Callable, List, Optional, Sequence
|
|
14
13
|
|
|
15
14
|
import matplotlib.pyplot as plt # type: ignore[import]
|
|
16
|
-
import matplotlib.cm as _cm # type: ignore[import]
|
|
17
15
|
from matplotlib import colors as mcolors # type: ignore[import]
|
|
18
|
-
from matplotlib.colors import LinearSegmentedColormap # type: ignore[import]
|
|
19
16
|
|
|
20
17
|
from ...plotting import apply_curve_color, update_labels
|
|
21
18
|
from ...ui import set_spine_side_color as _ui_set_spine_side_color
|
|
@@ -27,6 +24,7 @@ from ...color_utils import (
|
|
|
27
24
|
get_user_color_list,
|
|
28
25
|
resolve_color_token,
|
|
29
26
|
ensure_colormap,
|
|
27
|
+
get_colormap,
|
|
30
28
|
_CUSTOM_CMAPS,
|
|
31
29
|
)
|
|
32
30
|
from ..common.palettes import (
|
|
@@ -81,26 +79,7 @@ def run_xy_color_menu(
|
|
|
81
79
|
return resolve_palette_token(token, _palette_index)
|
|
82
80
|
|
|
83
81
|
def _apply_palette_to_lines(palette_name, indices):
|
|
84
|
-
|
|
85
|
-
cmap = None
|
|
86
|
-
try:
|
|
87
|
-
cmap = _cm.get_cmap(palette_name)
|
|
88
|
-
except Exception:
|
|
89
|
-
pass
|
|
90
|
-
if cmap is None and palette_name.lower().startswith('batlow'):
|
|
91
|
-
try:
|
|
92
|
-
_cmc = _il.import_module('cmcrameri.cm')
|
|
93
|
-
_attr = palette_name.lower()
|
|
94
|
-
cmap = getattr(_cmc, _attr, None) or getattr(_cmc, 'batlow', None)
|
|
95
|
-
except Exception:
|
|
96
|
-
pass
|
|
97
|
-
if cmap is None:
|
|
98
|
-
_base = palette_name.lower().rstrip('_r')
|
|
99
|
-
_cc = _CUSTOM_CMAPS.get(_base)
|
|
100
|
-
if _cc:
|
|
101
|
-
cmap = LinearSegmentedColormap.from_list(_base, _cc, N=256)
|
|
102
|
-
if palette_name.lower().endswith('_r'):
|
|
103
|
-
cmap = cmap.reversed()
|
|
82
|
+
cmap = get_colormap(palette_name)
|
|
104
83
|
if cmap is None:
|
|
105
84
|
print(f"Unknown palette '{palette_name}'.")
|
|
106
85
|
return
|
|
@@ -234,7 +213,7 @@ def run_xy_color_menu(
|
|
|
234
213
|
print("No valid indices parsed.")
|
|
235
214
|
continue
|
|
236
215
|
try:
|
|
237
|
-
cmap =
|
|
216
|
+
cmap = get_colormap(pal_name)
|
|
238
217
|
except Exception:
|
|
239
218
|
cmap = None
|
|
240
219
|
if cmap is None:
|
|
@@ -18,7 +18,7 @@ from matplotlib.ticker import ( # type: ignore[import]
|
|
|
18
18
|
NullLocator,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
-
from ...plotting import update_labels
|
|
21
|
+
from ...plotting import apply_curve_color, update_labels
|
|
22
22
|
from ...utils import (
|
|
23
23
|
normalize_label_text,
|
|
24
24
|
)
|
|
@@ -39,6 +39,8 @@ from ...ui import (
|
|
|
39
39
|
from .style import (
|
|
40
40
|
print_style_info as _bp_print_style_info,
|
|
41
41
|
apply_style_config as _bp_apply_style_config,
|
|
42
|
+
capture_xy_axis_style,
|
|
43
|
+
apply_xy_axis_style,
|
|
42
44
|
)
|
|
43
45
|
from ...config import load_config, save_config
|
|
44
46
|
from .style import export_style_config as _export_style_config
|
|
@@ -1262,7 +1264,9 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1262
1264
|
"rotation_angle": getattr(ax, '_rotation_angle', 0),
|
|
1263
1265
|
"stack_label_at_bottom": getattr(fig, '_stack_label_at_bottom', False),
|
|
1264
1266
|
"label_anchor_left": getattr(fig, '_label_anchor_left', False),
|
|
1265
|
-
"grid": any(line.get_visible() for line in ax.get_xgridlines() + ax.get_ygridlines())
|
|
1267
|
+
"grid": any(line.get_visible() for line in ax.get_xgridlines() + ax.get_ygridlines()),
|
|
1268
|
+
"curve_palettes": list(getattr(fig, '_curve_palette_history', []) or []),
|
|
1269
|
+
"axis_style": capture_xy_axis_style(ax),
|
|
1266
1270
|
}
|
|
1267
1271
|
# Optional per-set CIF visibility state for 1D mode
|
|
1268
1272
|
try:
|
|
@@ -1494,6 +1498,20 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1494
1498
|
except Exception:
|
|
1495
1499
|
pass
|
|
1496
1500
|
|
|
1501
|
+
# Tick/label colors and labelpads
|
|
1502
|
+
try:
|
|
1503
|
+
axis_style = snap.get("axis_style")
|
|
1504
|
+
if axis_style:
|
|
1505
|
+
spine_specs = {
|
|
1506
|
+
name: {"color": spec.get("color")}
|
|
1507
|
+
for name, spec in snap.get("spines", {}).items()
|
|
1508
|
+
}
|
|
1509
|
+
apply_xy_axis_style(ax, axis_style, fig=fig, spines_cfg=spine_specs)
|
|
1510
|
+
_ui_position_bottom_xlabel(ax, fig, tick_state)
|
|
1511
|
+
_ui_position_left_ylabel(ax, fig, tick_state)
|
|
1512
|
+
except Exception:
|
|
1513
|
+
pass
|
|
1514
|
+
|
|
1497
1515
|
# Labels list
|
|
1498
1516
|
labels[:] = snap["labels"]
|
|
1499
1517
|
|
|
@@ -1503,22 +1521,30 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1503
1521
|
i = item["index"]
|
|
1504
1522
|
ln = _line(i)
|
|
1505
1523
|
ln.set_data(item["x"], item["y"])
|
|
1506
|
-
ln.
|
|
1507
|
-
ln.
|
|
1508
|
-
ln.set_linestyle(item["ls"])
|
|
1524
|
+
ln.set_linewidth(item["lw"])
|
|
1525
|
+
ln.set_linestyle(item["ls"])
|
|
1509
1526
|
if item["marker"] is not None:
|
|
1510
|
-
ln.set_marker(item["marker"])
|
|
1527
|
+
ln.set_marker(item["marker"])
|
|
1511
1528
|
if item.get("markersize") is not None:
|
|
1512
|
-
try:
|
|
1513
|
-
|
|
1529
|
+
try:
|
|
1530
|
+
ln.set_markersize(item["markersize"])
|
|
1531
|
+
except Exception:
|
|
1532
|
+
pass
|
|
1533
|
+
if item["alpha"] is not None:
|
|
1534
|
+
ln.set_alpha(item["alpha"])
|
|
1535
|
+
apply_curve_color(ln, item["color"])
|
|
1514
1536
|
if item.get("mfc") is not None:
|
|
1515
|
-
try:
|
|
1516
|
-
|
|
1537
|
+
try:
|
|
1538
|
+
if str(item["mfc"]).lower() == "none":
|
|
1539
|
+
ln.set_markerfacecolor("none")
|
|
1540
|
+
except Exception:
|
|
1541
|
+
pass
|
|
1517
1542
|
if item.get("mec") is not None:
|
|
1518
|
-
try:
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1543
|
+
try:
|
|
1544
|
+
if str(item["mec"]).lower() == "none":
|
|
1545
|
+
ln.set_markeredgecolor("none")
|
|
1546
|
+
except Exception:
|
|
1547
|
+
pass
|
|
1522
1548
|
|
|
1523
1549
|
# Replace lists
|
|
1524
1550
|
x_data_list[:] = [np.array(a, copy=True) for a in snap["x_data_list"]]
|
|
@@ -1602,6 +1628,20 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1602
1628
|
if 'label_anchor_left' in snap:
|
|
1603
1629
|
fig._label_anchor_left = bool(snap['label_anchor_left'])
|
|
1604
1630
|
|
|
1631
|
+
if snap.get("curve_palettes"):
|
|
1632
|
+
fig._curve_palette_history = [
|
|
1633
|
+
{
|
|
1634
|
+
'palette': rec.get('palette'),
|
|
1635
|
+
'indices': list(rec.get('indices', [])),
|
|
1636
|
+
'low_clip': float(rec.get('low_clip', 0.08)),
|
|
1637
|
+
'high_clip': float(rec.get('high_clip', 0.85)),
|
|
1638
|
+
}
|
|
1639
|
+
for rec in snap["curve_palettes"]
|
|
1640
|
+
if rec.get('palette') and rec.get('indices')
|
|
1641
|
+
]
|
|
1642
|
+
elif hasattr(fig, '_curve_palette_history'):
|
|
1643
|
+
delattr(fig, '_curve_palette_history')
|
|
1644
|
+
|
|
1605
1645
|
# Restore grid state
|
|
1606
1646
|
if 'grid' in snap:
|
|
1607
1647
|
try:
|