batplot 1.8.44__tar.gz → 1.8.45__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.44 → batplot-1.8.45}/PKG-INFO +1 -1
- {batplot-1.8.44 → batplot-1.8.45}/batplot/__init__.py +1 -1
- batplot-1.8.45/batplot/_mpl_backend.py +231 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/batch.py +16 -11
- {batplot-1.8.44 → batplot-1.8.45}/batplot/batplot.py +4 -62
- {batplot-1.8.44 → batplot-1.8.45}/batplot/canvas_interactive.py +4 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/cli.py +2 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/color_utils.py +6 -21
- {batplot-1.8.44 → batplot-1.8.45}/batplot/converters.py +3 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/data/CHANGELOG.md +4 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/dev_upgrade.py +1 -1
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/palettes.py +5 -3
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/spines.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/colors.py +4 -3
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/interactive.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/legend.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/routing.py +14 -34
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/colors.py +17 -10
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/dqdv_2d.py +1 -1
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/interactive.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/routing.py +37 -81
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/style.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/colors.py +4 -5
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/interactive.py +8 -6
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/labels.py +1 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/plot.py +12 -9
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/routing.py +4 -25
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/style.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/session_routing.py +11 -1
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/interactive.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/pipeline.py +15 -14
- {batplot-1.8.44 → batplot-1.8.45}/batplot/readers.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot/session.py +7 -7
- {batplot-1.8.44 → batplot-1.8.45}/batplot/version_check.py +2 -2
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/PKG-INFO +1 -1
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/SOURCES.txt +2 -0
- {batplot-1.8.44 → batplot-1.8.45}/pyproject.toml +7 -1
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_cpc_roundtrip.py +9 -1
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_ec_roundtrip.py +1 -1
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_interactive_menu_smoke.py +2 -2
- batplot-1.8.45/tests/test_mpl_backend.py +104 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_operando_roundtrip.py +3 -3
- {batplot-1.8.44 → batplot-1.8.45}/DEVELOPING.md +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/LICENSE +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/MANIFEST.in +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/NOTICE +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/README.md +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/args.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/cif.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/config.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/ec_common.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/modes.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/axis_state.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/files.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/fonts.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/interactive_state.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/menu_rendering.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/menus.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/smoothing.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/sources.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/terminal.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/common/title_offsets.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/actions.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/labels.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/menu.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/session.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/cpc/snapshots.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/actions.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/export.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/labels.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/legend.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/legend_order.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/line_style.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/menu.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/session.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/electrochem/spine_colors.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/actions.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/grid.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/ions_axis.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/layout.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/line_style.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/menu.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/peaks.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/session.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/operando/visibility.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/__init__.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/actions.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/arrange.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/axis_range.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/cif.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/colors.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/data_ops.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/derivative.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/game.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/labels.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/line_style.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/menu.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/peaks.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/session.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/smoothing.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plot_modes/xy/style.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/plotting.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/showcol.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/style.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/ui.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot/utils.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/batplot.egg-info/top_level.txt +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/setup.cfg +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/setup.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_cli_smoke.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_color_utils.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_common_files.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_common_palettes.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_contracts.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_csv_readers.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_dev_upgrade.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_interactive_state.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/tests/test_xy_modules.py +0 -0
- {batplot-1.8.44 → batplot-1.8.45}/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.45
|
|
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
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Matplotlib backend selection for batplot CLI and interactive sessions.
|
|
2
|
+
|
|
3
|
+
Imported early from :mod:`batplot.cli` so pytest/CI on Windows never fall back
|
|
4
|
+
to Tk when ``MPLBACKEND=Agg`` is set explicitly in the environment.
|
|
5
|
+
|
|
6
|
+
When the user has not set ``MPLBACKEND``, we start on Agg for safe imports but
|
|
7
|
+
may switch to a GUI backend before opening interactive windows (``.pkl`` reload,
|
|
8
|
+
``--gc --i``, ``--canvas``, etc.).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import importlib.util
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
_USER_SET_MPLBACKEND = "MPLBACKEND" in os.environ
|
|
18
|
+
_DEFAULT_BACKEND = os.environ.get("MPLBACKEND", "Agg")
|
|
19
|
+
|
|
20
|
+
import matplotlib as _mpl
|
|
21
|
+
|
|
22
|
+
_mpl.use(_DEFAULT_BACKEND, force=True)
|
|
23
|
+
|
|
24
|
+
_INTERACTIVE_BACKENDS = frozenset(
|
|
25
|
+
{
|
|
26
|
+
"macosx",
|
|
27
|
+
"tkagg",
|
|
28
|
+
"qt5agg",
|
|
29
|
+
"qt4agg",
|
|
30
|
+
"qtagg",
|
|
31
|
+
"wxagg",
|
|
32
|
+
"gtk3agg",
|
|
33
|
+
"gtk4agg",
|
|
34
|
+
"wx",
|
|
35
|
+
"qt",
|
|
36
|
+
"gtk",
|
|
37
|
+
"gtk3",
|
|
38
|
+
"gtk4",
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _is_noninteractive_backend(name: str | None) -> bool:
|
|
44
|
+
if not isinstance(name, str):
|
|
45
|
+
return False
|
|
46
|
+
low = name.lower()
|
|
47
|
+
if low in _INTERACTIVE_BACKENDS:
|
|
48
|
+
return False
|
|
49
|
+
return ("agg" in low) or ("inline" in low) or (low in {"pdf", "ps", "svg", "template"})
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_interactive_backend() -> bool:
|
|
53
|
+
"""Return True when the active Matplotlib backend can open a GUI window."""
|
|
54
|
+
try:
|
|
55
|
+
return not _is_noninteractive_backend(_mpl.get_backend())
|
|
56
|
+
except Exception:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _headless_context() -> bool:
|
|
61
|
+
"""True when batplot must stay on a non-interactive backend (CI/pytest)."""
|
|
62
|
+
if os.environ.get("BATPLOT_HEADLESS", "").lower() in ("1", "true", "yes"):
|
|
63
|
+
return True
|
|
64
|
+
if os.environ.get("CI", "").lower() in ("1", "true", "yes"):
|
|
65
|
+
return True
|
|
66
|
+
if os.environ.get("PYTEST_CURRENT_TEST"):
|
|
67
|
+
return True
|
|
68
|
+
if "pytest" in sys.modules:
|
|
69
|
+
return True
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def running_headless() -> bool:
|
|
74
|
+
"""True under pytest, CI, or when ``BATPLOT_HEADLESS`` is set."""
|
|
75
|
+
return _headless_context()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def wants_interactive_window(args) -> bool:
|
|
79
|
+
"""Return True when this CLI invocation should open an interactive figure."""
|
|
80
|
+
if getattr(args, "all", None) is not None:
|
|
81
|
+
return False
|
|
82
|
+
files = getattr(args, "files", None) or []
|
|
83
|
+
if any(str(f).lower().endswith(".pkl") for f in files):
|
|
84
|
+
return True
|
|
85
|
+
if getattr(args, "canvas", False):
|
|
86
|
+
return True
|
|
87
|
+
if getattr(args, "interactive", False):
|
|
88
|
+
return True
|
|
89
|
+
# Headless export: mode flags with --out/--savefig do not need a GUI window.
|
|
90
|
+
if getattr(args, "savefig", False) or getattr(args, "out", None):
|
|
91
|
+
return False
|
|
92
|
+
for attr in ("operando", "contour", "gc", "cv", "dqdv", "cpc", "epc"):
|
|
93
|
+
if getattr(args, attr, False):
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _should_respect_env_agg() -> bool:
|
|
99
|
+
"""Keep Agg when the environment requested it and we are in headless context."""
|
|
100
|
+
if not _USER_SET_MPLBACKEND:
|
|
101
|
+
return False
|
|
102
|
+
env_be = os.environ.get("MPLBACKEND")
|
|
103
|
+
if not env_be or not _is_noninteractive_backend(env_be):
|
|
104
|
+
return False
|
|
105
|
+
return _headless_context()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _has_qt_bindings() -> bool:
|
|
109
|
+
for mod in ("PyQt6", "PyQt5", "PySide6", "PySide2"):
|
|
110
|
+
if importlib.util.find_spec(mod) is not None:
|
|
111
|
+
return True
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _gui_backend_order() -> list[str]:
|
|
116
|
+
if sys.platform == "darwin":
|
|
117
|
+
return ["MacOSX", "TkAgg", "QtAgg"]
|
|
118
|
+
if sys.platform.startswith("win"):
|
|
119
|
+
return ["TkAgg", "QtAgg"]
|
|
120
|
+
return ["TkAgg", "QtAgg", "Gtk4Agg", "Gtk3Agg"]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _backend_can_be_tried(name: str) -> bool:
|
|
124
|
+
if name == "TkAgg":
|
|
125
|
+
return importlib.util.find_spec("tkinter") is not None
|
|
126
|
+
if name == "QtAgg":
|
|
127
|
+
return _has_qt_bindings()
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _backend_setup_tips() -> list[str]:
|
|
132
|
+
if sys.platform == "darwin":
|
|
133
|
+
return [
|
|
134
|
+
" export MPLBACKEND=MacOSX # built-in macOS backend",
|
|
135
|
+
" export MPLBACKEND=TkAgg # if Tk is available",
|
|
136
|
+
" export MPLBACKEND=QtAgg # if PyQt/PySide is installed",
|
|
137
|
+
]
|
|
138
|
+
if sys.platform.startswith("win"):
|
|
139
|
+
return [
|
|
140
|
+
" set MPLBACKEND=TkAgg # cmd, if Tk is available",
|
|
141
|
+
' $env:MPLBACKEND="TkAgg" # PowerShell',
|
|
142
|
+
" set MPLBACKEND=QtAgg # if PyQt/PySide is installed",
|
|
143
|
+
]
|
|
144
|
+
return [
|
|
145
|
+
" export MPLBACKEND=TkAgg # if python3-tk is installed",
|
|
146
|
+
" export MPLBACKEND=QtAgg # if PyQt/PySide is installed",
|
|
147
|
+
" export MPLBACKEND=Gtk3Agg # if GTK3 bindings are installed",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def ensure_gui_backend(args=None) -> bool:
|
|
152
|
+
"""Switch from Agg to a GUI backend when an interactive window is expected.
|
|
153
|
+
|
|
154
|
+
Returns True when the active backend is interactive (or was switched to one).
|
|
155
|
+
Honors ``MPLBACKEND=Agg`` only under pytest/CI; interactive CLI use (``--i``,
|
|
156
|
+
``.pkl`` reload) overrides an inherited Agg default from conda/shell profiles.
|
|
157
|
+
"""
|
|
158
|
+
if args is not None and not wants_interactive_window(args):
|
|
159
|
+
return is_interactive_backend()
|
|
160
|
+
|
|
161
|
+
if _should_respect_env_agg():
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
if _USER_SET_MPLBACKEND:
|
|
165
|
+
env_be = os.environ.get("MPLBACKEND")
|
|
166
|
+
if env_be and not _is_noninteractive_backend(env_be):
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
if is_interactive_backend():
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
for cand in _gui_backend_order():
|
|
173
|
+
if not _backend_can_be_tried(cand):
|
|
174
|
+
continue
|
|
175
|
+
try:
|
|
176
|
+
_mpl.use(cand, force=True)
|
|
177
|
+
if is_interactive_backend():
|
|
178
|
+
return True
|
|
179
|
+
except Exception:
|
|
180
|
+
continue
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def warn_if_noninteractive(context: str = "interactive menu") -> bool:
|
|
185
|
+
"""Print a helpful message when the backend cannot show a window."""
|
|
186
|
+
if is_interactive_backend():
|
|
187
|
+
return True
|
|
188
|
+
try:
|
|
189
|
+
backend = _mpl.get_backend()
|
|
190
|
+
except Exception:
|
|
191
|
+
backend = "unknown"
|
|
192
|
+
print(f"Matplotlib backend '{backend}' is non-interactive; a window cannot be shown.")
|
|
193
|
+
print("Tips: unset MPLBACKEND or set a GUI backend:")
|
|
194
|
+
for line in _backend_setup_tips():
|
|
195
|
+
print(line)
|
|
196
|
+
if context:
|
|
197
|
+
print(f"The {context} requires an interactive backend.")
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def require_interactive_display(
|
|
202
|
+
args=None,
|
|
203
|
+
*,
|
|
204
|
+
context: str = "interactive menu",
|
|
205
|
+
export_hint: str | None = "Or run without --interactive and use --out to save the figure.",
|
|
206
|
+
) -> bool:
|
|
207
|
+
"""Ensure a GUI backend, warn if unavailable, return True when display is possible."""
|
|
208
|
+
ensure_gui_backend(args)
|
|
209
|
+
if is_interactive_backend():
|
|
210
|
+
return True
|
|
211
|
+
warn_if_noninteractive(context)
|
|
212
|
+
if export_hint:
|
|
213
|
+
print(export_hint)
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def show_figure_if_possible(args=None, *, block: bool = False) -> bool:
|
|
218
|
+
"""Show the current figure when the backend supports windows."""
|
|
219
|
+
ensure_gui_backend(args)
|
|
220
|
+
if not is_interactive_backend():
|
|
221
|
+
if args is not None and not (getattr(args, "savefig", False) or getattr(args, "out", None)):
|
|
222
|
+
try:
|
|
223
|
+
backend = _mpl.get_backend()
|
|
224
|
+
except Exception:
|
|
225
|
+
backend = "unknown"
|
|
226
|
+
print(f"Matplotlib backend '{backend}' is non-interactive; use --out to save the figure.")
|
|
227
|
+
return False
|
|
228
|
+
import matplotlib.pyplot as plt
|
|
229
|
+
|
|
230
|
+
plt.show(block=block)
|
|
231
|
+
return True
|
|
@@ -6,9 +6,10 @@ import os
|
|
|
6
6
|
import json
|
|
7
7
|
from typing import Tuple, cast
|
|
8
8
|
|
|
9
|
-
import matplotlib.cm as cm # type: ignore[import]
|
|
10
|
-
import numpy as np # type: ignore[import]
|
|
11
9
|
import matplotlib.pyplot as plt # type: ignore[import]
|
|
10
|
+
import numpy as np # type: ignore[import]
|
|
11
|
+
|
|
12
|
+
from .color_utils import get_colormap
|
|
12
13
|
|
|
13
14
|
from .readers import (
|
|
14
15
|
read_gr_file,
|
|
@@ -961,9 +962,11 @@ def batch_process_ec(directory: str, args):
|
|
|
961
962
|
|
|
962
963
|
if remaining <= 60:
|
|
963
964
|
# Use tab20, tab20b, tab20c for categorical colors (up to 70 total)
|
|
964
|
-
tab20 =
|
|
965
|
-
tab20b =
|
|
966
|
-
tab20c =
|
|
965
|
+
tab20 = get_colormap('tab20')
|
|
966
|
+
tab20b = get_colormap('tab20b')
|
|
967
|
+
tab20c = get_colormap('tab20c')
|
|
968
|
+
if tab20 is None or tab20b is None or tab20c is None:
|
|
969
|
+
return colors[:n_colors]
|
|
967
970
|
|
|
968
971
|
for i in range(remaining):
|
|
969
972
|
cmap_idx = i % 60
|
|
@@ -987,7 +990,9 @@ def batch_process_ec(directory: str, args):
|
|
|
987
990
|
colors_per_map = (remaining + len(cmaps) - 1) // len(cmaps)
|
|
988
991
|
|
|
989
992
|
for cmap_name in cmaps:
|
|
990
|
-
cmap =
|
|
993
|
+
cmap = get_colormap(cmap_name)
|
|
994
|
+
if cmap is None:
|
|
995
|
+
continue
|
|
991
996
|
# Sample evenly across the colormap
|
|
992
997
|
for i in range(colors_per_map):
|
|
993
998
|
if len(colors) >= n_colors:
|
|
@@ -1117,7 +1122,7 @@ def batch_process_ec(directory: str, args):
|
|
|
1117
1122
|
cycle_colors = get_color_palette(len(cycles_present))
|
|
1118
1123
|
|
|
1119
1124
|
for idx, cyc in enumerate(cycles_present): # Plot all cycles
|
|
1120
|
-
mask = (cyc_int == cyc)
|
|
1125
|
+
mask = np.asarray(cyc_int == cyc, dtype=bool)
|
|
1121
1126
|
mask_idx = np.where(mask)[0]
|
|
1122
1127
|
if mask_idx.size >= 2:
|
|
1123
1128
|
color = cycle_colors[idx]
|
|
@@ -1265,11 +1270,11 @@ def batch_process_ec(directory: str, args):
|
|
|
1265
1270
|
mask_c = (cyc_int_raw == cyc) & charge_mask
|
|
1266
1271
|
mask_d = (cyc_int_raw == cyc) & discharge_mask
|
|
1267
1272
|
if np.count_nonzero(mask_c) >= 2:
|
|
1268
|
-
en_c = float(np.trapz(voltage[mask_c], cap_x[mask_c]))
|
|
1273
|
+
en_c = float((getattr(np, "trapezoid", None) or np.trapz)(voltage[mask_c], cap_x[mask_c]))
|
|
1269
1274
|
else:
|
|
1270
1275
|
en_c = 0.0
|
|
1271
1276
|
if np.count_nonzero(mask_d) >= 2:
|
|
1272
|
-
en_d = float(np.trapz(voltage[mask_d], cap_x[mask_d]))
|
|
1277
|
+
en_d = float((getattr(np, "trapezoid", None) or np.trapz)(voltage[mask_d], cap_x[mask_d]))
|
|
1273
1278
|
else:
|
|
1274
1279
|
en_d = 0.0
|
|
1275
1280
|
en_charge.append(en_c)
|
|
@@ -1300,11 +1305,11 @@ def batch_process_ec(directory: str, args):
|
|
|
1300
1305
|
val_d = np.max(cap_x[mask_d]) if np.any(mask_d) else 0.0
|
|
1301
1306
|
else:
|
|
1302
1307
|
if np.count_nonzero(mask_c) >= 2:
|
|
1303
|
-
val_c = float(np.trapz(voltage[mask_c], cap_x[mask_c]))
|
|
1308
|
+
val_c = float((getattr(np, "trapezoid", None) or np.trapz)(voltage[mask_c], cap_x[mask_c]))
|
|
1304
1309
|
else:
|
|
1305
1310
|
val_c = 0.0
|
|
1306
1311
|
if np.count_nonzero(mask_d) >= 2:
|
|
1307
|
-
val_d = float(np.trapz(voltage[mask_d], cap_x[mask_d]))
|
|
1312
|
+
val_d = float((getattr(np, "trapezoid", None) or np.trapz)(voltage[mask_d], cap_x[mask_d]))
|
|
1308
1313
|
else:
|
|
1309
1314
|
val_d = 0.0
|
|
1310
1315
|
cap_charge_list.append(val_c)
|
|
@@ -421,6 +421,10 @@ def batplot_main() -> int: # type: ignore
|
|
|
421
421
|
print("Use 'batplot --v' for version and release info, 'batplot --h' for CLI help, or 'batplot --m' to open the user manual.")
|
|
422
422
|
return 0 # Exit successfully (not an error, just nothing to do)
|
|
423
423
|
|
|
424
|
+
from ._mpl_backend import ensure_gui_backend
|
|
425
|
+
|
|
426
|
+
ensure_gui_backend(args)
|
|
427
|
+
|
|
424
428
|
# ====================================================================
|
|
425
429
|
# STEP 3: ROUTE TO APPROPRIATE MODE HANDLER
|
|
426
430
|
# ====================================================================
|
|
@@ -482,64 +486,6 @@ def batplot_main() -> int: # type: ignore
|
|
|
482
486
|
"""
|
|
483
487
|
|
|
484
488
|
|
|
485
|
-
# Ensure an interactive (GUI) backend when a window is expected (interactive mode or operando/GC plots)
|
|
486
|
-
def _ensure_gui_backend_for_interactive():
|
|
487
|
-
try:
|
|
488
|
-
argv = sys.argv
|
|
489
|
-
except Exception:
|
|
490
|
-
argv = []
|
|
491
|
-
# Trigger if interactive is requested OR when operando/GC plotting likely calls show()
|
|
492
|
-
wants_interactive = any(flag in argv for flag in ("--interactive",))
|
|
493
|
-
wants_interactive = wants_interactive or ("--operando" in argv or "--contour" in argv)
|
|
494
|
-
wants_interactive = wants_interactive or ("--gc" in argv)
|
|
495
|
-
if not wants_interactive:
|
|
496
|
-
return
|
|
497
|
-
# If MPLBACKEND is set to a GUI backend, respect it; if it's non-interactive, we'll override below
|
|
498
|
-
env_be = os.environ.get("MPLBACKEND")
|
|
499
|
-
if env_be:
|
|
500
|
-
low = env_be.lower()
|
|
501
|
-
if low in {"macosx","tkagg","qtagg"}:
|
|
502
|
-
return
|
|
503
|
-
try:
|
|
504
|
-
be = _mpl.get_backend()
|
|
505
|
-
except Exception:
|
|
506
|
-
be = None
|
|
507
|
-
def _is_noninteractive(name):
|
|
508
|
-
if not isinstance(name, str):
|
|
509
|
-
return False
|
|
510
|
-
low = name.lower()
|
|
511
|
-
return ("agg" in low) or ("inline" in low) or (low in {"pdf","ps","svg","template"})
|
|
512
|
-
if not _is_noninteractive(be):
|
|
513
|
-
return
|
|
514
|
-
# Try GUI backends in order of likelihood
|
|
515
|
-
candidates = [
|
|
516
|
-
("darwin", ["MacOSX", "TkAgg", "QtAgg"]),
|
|
517
|
-
("win", ["TkAgg", "QtAgg"]),
|
|
518
|
-
("other", ["TkAgg", "QtAgg"]),
|
|
519
|
-
]
|
|
520
|
-
plat = sys.platform
|
|
521
|
-
if plat == "darwin":
|
|
522
|
-
order = candidates[0][1]
|
|
523
|
-
elif plat.startswith("win"):
|
|
524
|
-
order = candidates[1][1]
|
|
525
|
-
else:
|
|
526
|
-
order = candidates[2][1]
|
|
527
|
-
for cand in order:
|
|
528
|
-
try:
|
|
529
|
-
if cand == "TkAgg":
|
|
530
|
-
if importlib.util.find_spec("tkinter") is None:
|
|
531
|
-
continue
|
|
532
|
-
elif cand == "QtAgg":
|
|
533
|
-
if (importlib.util.find_spec("PyQt5") is None) and (importlib.util.find_spec("PySide6") is None):
|
|
534
|
-
continue
|
|
535
|
-
# MacOSX: attempt; will fail on non-framework builds
|
|
536
|
-
_mpl.use(cand, force=True)
|
|
537
|
-
break
|
|
538
|
-
except Exception:
|
|
539
|
-
continue
|
|
540
|
-
|
|
541
|
-
_ensure_gui_backend_for_interactive()
|
|
542
|
-
|
|
543
489
|
# Set global default font
|
|
544
490
|
plt.rcParams.update({
|
|
545
491
|
'font.family': 'sans-serif',
|
|
@@ -550,10 +496,6 @@ def batplot_main() -> int: # type: ignore
|
|
|
550
496
|
'font.size': 16
|
|
551
497
|
})
|
|
552
498
|
|
|
553
|
-
# Parse CLI arguments early; many top-level branches depend on args
|
|
554
|
-
args = _bp_parse_args()
|
|
555
|
-
|
|
556
|
-
|
|
557
499
|
"""
|
|
558
500
|
Note: CIF parsing and simulation helpers now come from batplot.cif.
|
|
559
501
|
This file defers to simulate_cif_pattern_Q and cif_reflection_positions
|
|
@@ -25,6 +25,7 @@ from matplotlib.patches import Rectangle
|
|
|
25
25
|
from matplotlib.text import Text
|
|
26
26
|
|
|
27
27
|
from .session import load_ec_session, load_operando_session, load_cpc_session, load_xy_session
|
|
28
|
+
from ._mpl_backend import ensure_gui_backend
|
|
28
29
|
from .plot_modes.electrochem.interactive import electrochem_interactive_menu
|
|
29
30
|
from .plot_modes.xy.interactive import interactive_menu, normalize_xy_menu_kwargs
|
|
30
31
|
from .utils import _confirm_overwrite
|
|
@@ -290,6 +291,7 @@ def _normalize_loaded_annotation(raw: Any, base_dir: Optional[str]) -> Optional[
|
|
|
290
291
|
|
|
291
292
|
|
|
292
293
|
def run_canvas_mode(pkl_paths: List[str]) -> None:
|
|
294
|
+
ensure_gui_backend(None)
|
|
293
295
|
pkl_paths, loaded_positions, raw_annotations, manifest_base_dir = _expand_canvas_paths(pkl_paths)
|
|
294
296
|
|
|
295
297
|
if not pkl_paths:
|
|
@@ -465,7 +467,7 @@ def run_canvas_mode(pkl_paths: List[str]) -> None:
|
|
|
465
467
|
ann_runtime.append((None, None))
|
|
466
468
|
continue
|
|
467
469
|
try:
|
|
468
|
-
ax_img = fig_canvas.add_axes(
|
|
470
|
+
ax_img = fig_canvas.add_axes((l, b, w, h), facecolor='none')
|
|
469
471
|
ax_img.set_zorder(15)
|
|
470
472
|
if arr.ndim == 2:
|
|
471
473
|
ax_img.imshow(arr, cmap='gray', aspect='auto', origin='upper')
|
|
@@ -724,7 +726,7 @@ def run_canvas_mode(pkl_paths: List[str]) -> None:
|
|
|
724
726
|
continue
|
|
725
727
|
try:
|
|
726
728
|
arr_e = mpl_imread(ip)
|
|
727
|
-
ax_e = fig_exp.add_axes(
|
|
729
|
+
ax_e = fig_exp.add_axes((l, b, w, h), facecolor='none')
|
|
728
730
|
ax_e.set_zorder(15)
|
|
729
731
|
if arr_e.ndim == 2:
|
|
730
732
|
ax_e.imshow(arr_e, cmap='gray', aspect='auto', origin='upper')
|
|
@@ -41,7 +41,7 @@ This is useful when you want the color order flipped.
|
|
|
41
41
|
|
|
42
42
|
from __future__ import annotations
|
|
43
43
|
|
|
44
|
-
from typing import List, Optional, Sequence
|
|
44
|
+
from typing import List, Optional, Sequence, cast
|
|
45
45
|
|
|
46
46
|
import matplotlib.pyplot as plt # type: ignore[import]
|
|
47
47
|
from matplotlib import colors as mcolors # type: ignore[import]
|
|
@@ -171,7 +171,9 @@ def ensure_colormap(name: Optional[str]) -> bool:
|
|
|
171
171
|
# STEP 4: Final fallback - try to get it directly from matplotlib
|
|
172
172
|
# This handles any other matplotlib-compatible colormap
|
|
173
173
|
try:
|
|
174
|
-
|
|
174
|
+
from matplotlib import colormaps as mpl_colormaps
|
|
175
|
+
|
|
176
|
+
_ = mpl_colormaps[base_lower]
|
|
175
177
|
return True
|
|
176
178
|
except Exception:
|
|
177
179
|
return False
|
|
@@ -201,7 +203,7 @@ def get_colormap(name: Optional[str]) -> Optional[Colormap]:
|
|
|
201
203
|
if callable(registry_get):
|
|
202
204
|
for candidate in candidates:
|
|
203
205
|
try:
|
|
204
|
-
return registry_get(candidate)
|
|
206
|
+
return cast(Colormap, registry_get(candidate))
|
|
205
207
|
except Exception:
|
|
206
208
|
pass
|
|
207
209
|
for candidate in candidates:
|
|
@@ -313,24 +315,7 @@ def palette_preview(name: str, steps: int = 8) -> str:
|
|
|
313
315
|
ensure_colormap(name)
|
|
314
316
|
|
|
315
317
|
# Try to get the colormap from matplotlib
|
|
316
|
-
cmap: Optional[Colormap]
|
|
317
|
-
try:
|
|
318
|
-
cmap = plt.get_cmap(name)
|
|
319
|
-
except Exception:
|
|
320
|
-
cmap = None
|
|
321
|
-
# Fallback: try cmcrameri if it's a batlow variant
|
|
322
|
-
lower = name.lower()
|
|
323
|
-
if lower.startswith('batlow'):
|
|
324
|
-
try:
|
|
325
|
-
import cmcrameri.cm as cmc # type: ignore[import]
|
|
326
|
-
if hasattr(cmc, lower):
|
|
327
|
-
cmap = getattr(cmc, lower)
|
|
328
|
-
elif hasattr(cmc, 'batlow'):
|
|
329
|
-
cmap = getattr(cmc, 'batlow')
|
|
330
|
-
except Exception:
|
|
331
|
-
return ""
|
|
332
|
-
else:
|
|
333
|
-
return ""
|
|
318
|
+
cmap: Optional[Colormap] = get_colormap(name)
|
|
334
319
|
|
|
335
320
|
# If we still don't have a colormap, bail out
|
|
336
321
|
if cmap is None:
|
|
@@ -271,6 +271,7 @@ def convert_xrd_data(
|
|
|
271
271
|
|
|
272
272
|
# Perform conversion based on type
|
|
273
273
|
if conversion_type == "wavelength_to_wavelength":
|
|
274
|
+
assert from_wl is not None and to_wl is not None
|
|
274
275
|
# Step 1: Convert 2θ (from_wl) → Q
|
|
275
276
|
theta_rad = np.radians(x / 2) # Convert 2θ to θ in radians
|
|
276
277
|
q = 4 * np.pi * np.sin(theta_rad) / from_wl
|
|
@@ -287,12 +288,14 @@ def convert_xrd_data(
|
|
|
287
288
|
output_ext = os.path.splitext(fname)[1] # Keep original extension
|
|
288
289
|
|
|
289
290
|
elif conversion_type == "wavelength_to_q":
|
|
291
|
+
assert from_wl is not None
|
|
290
292
|
# Convert 2θ → Q
|
|
291
293
|
theta_rad = np.radians(x / 2) # Convert 2θ to θ in radians
|
|
292
294
|
x_new = 4 * np.pi * np.sin(theta_rad) / from_wl
|
|
293
295
|
output_ext = ".qye"
|
|
294
296
|
|
|
295
297
|
elif conversion_type == "q_to_wavelength":
|
|
298
|
+
assert to_wl is not None
|
|
296
299
|
# Convert Q → 2θ
|
|
297
300
|
# Q = 4π sin(θ) / λ, so sin(θ) = Q × λ / (4π)
|
|
298
301
|
sin_theta = x * to_wl / (4 * np.pi)
|
|
@@ -598,7 +598,7 @@ def _required_package_data_files(project_root: Path) -> list[str]:
|
|
|
598
598
|
return []
|
|
599
599
|
rel_paths: list[str] = []
|
|
600
600
|
for item in re.findall(r'"([^"]+)"', package_match.group("items")):
|
|
601
|
-
rel_paths.append(
|
|
601
|
+
rel_paths.append("batplot/" + item.replace("\\", "/"))
|
|
602
602
|
return sorted(set(rel_paths))
|
|
603
603
|
|
|
604
604
|
|
|
@@ -9,12 +9,14 @@ keeps its own established defaults.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from typing import Any, Callable, Dict, Iterable, List, Sequence, Tuple
|
|
12
|
+
from typing import Any, Callable, Dict, Iterable, List, Sequence, Tuple, cast
|
|
13
13
|
|
|
14
14
|
import numpy as np
|
|
15
15
|
import matplotlib.pyplot as plt
|
|
16
16
|
import matplotlib.colors as mcolors
|
|
17
17
|
|
|
18
|
+
from ...color_utils import get_colormap
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
TAB10_HEX: Tuple[str, ...] = (
|
|
20
22
|
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
|
|
@@ -181,7 +183,7 @@ def sample_palette_colors(
|
|
|
181
183
|
if not ensure_colormap(name):
|
|
182
184
|
name = "viridis"
|
|
183
185
|
ensure_colormap(name)
|
|
184
|
-
cmap_getter = get_cmap or
|
|
186
|
+
cmap_getter = get_cmap or get_colormap
|
|
185
187
|
try:
|
|
186
188
|
cmap = cmap_getter(name)
|
|
187
189
|
except Exception:
|
|
@@ -195,7 +197,7 @@ def sample_palette_colors(
|
|
|
195
197
|
if isinstance(rgb, tuple) and len(rgb) >= 3:
|
|
196
198
|
out.append(mcolors.rgb2hex(rgb[:3]))
|
|
197
199
|
else:
|
|
198
|
-
out.append(mcolors.to_hex(rgb))
|
|
200
|
+
out.append(mcolors.to_hex(cast(Any, rgb)))
|
|
199
201
|
return out
|
|
200
202
|
sampled = sample_colormap(cmap, n, single=single, pair=pair, span=span)
|
|
201
203
|
return [mcolors.rgb2hex(color[:3]) for color in sampled]
|
|
@@ -7,7 +7,7 @@ application that is identical across modes.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from typing import Any, Callable, Dict, Iterable, Mapping, MutableMapping, Optional, Sequence
|
|
10
|
+
from typing import Any, Callable, Dict, Iterable, Mapping, MutableMapping, Optional, Sequence, cast
|
|
11
11
|
|
|
12
12
|
import matplotlib.pyplot as plt # type: ignore[import-untyped]
|
|
13
13
|
from matplotlib.ticker import ( # type: ignore[import-untyped]
|
|
@@ -340,7 +340,7 @@ def current_tick_width(axis_obj: Any, which: str) -> Optional[float]:
|
|
|
340
340
|
width = tick_kw.get("width")
|
|
341
341
|
if width is None:
|
|
342
342
|
axis_name = getattr(axis_obj, "axis_name", "x")
|
|
343
|
-
width = plt.rcParams.get(f"{axis_name}tick.{which}.width")
|
|
343
|
+
width = plt.rcParams.get(cast(Any, f"{axis_name}tick.{which}.width"))
|
|
344
344
|
return float(width) if width is not None else None
|
|
345
345
|
except Exception:
|
|
346
346
|
return None
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import matplotlib.cm as cm # type: ignore[import]
|
|
6
5
|
from matplotlib.colors import hsv_to_rgb, rgb_to_hsv, to_rgb # type: ignore[import]
|
|
7
6
|
|
|
8
7
|
from ...color_utils import (
|
|
9
8
|
color_block,
|
|
10
9
|
ensure_colormap,
|
|
10
|
+
get_colormap,
|
|
11
11
|
get_user_color_list,
|
|
12
12
|
manage_user_colors,
|
|
13
13
|
palette_preview,
|
|
@@ -41,7 +41,7 @@ def cpc_palette_color(name: str, idx: int = 0, total: int = 1):
|
|
|
41
41
|
name,
|
|
42
42
|
max(total, 1),
|
|
43
43
|
ensure_colormap=ensure_colormap,
|
|
44
|
-
get_cmap=
|
|
44
|
+
get_cmap=get_colormap,
|
|
45
45
|
prefer_listed_colors=True,
|
|
46
46
|
pair=(0.15, 0.85),
|
|
47
47
|
span=(0.08, 0.88),
|
|
@@ -59,7 +59,8 @@ def parse_file_range_palette(tokens: list[str], n_files: int, palette_opts: list
|
|
|
59
59
|
try:
|
|
60
60
|
if not ensure_colormap(last):
|
|
61
61
|
raise ValueError(last)
|
|
62
|
-
|
|
62
|
+
if get_colormap(last) is None:
|
|
63
|
+
raise ValueError(last)
|
|
63
64
|
palette = last
|
|
64
65
|
except Exception:
|
|
65
66
|
if last.isdigit() and 1 <= int(last) <= len(palette_opts):
|
|
@@ -33,7 +33,7 @@ This is useful for comparing different battery cells, materials, or conditions.
|
|
|
33
33
|
"""
|
|
34
34
|
from __future__ import annotations
|
|
35
35
|
|
|
36
|
-
from typing import Any, Dict, List, Optional
|
|
36
|
+
from typing import Any, Dict, List, Optional, cast
|
|
37
37
|
import json
|
|
38
38
|
import os
|
|
39
39
|
import sys
|
|
@@ -226,7 +226,7 @@ def _style_snapshot(fig, ax, ax2, sc_charge, sc_discharge, sc_eff, file_data=Non
|
|
|
226
226
|
if width is None:
|
|
227
227
|
axis_name = getattr(axis_obj, 'axis_name', 'x')
|
|
228
228
|
rc_key = f"{axis_name}tick.{which}.width"
|
|
229
|
-
width = plt.rcParams.get(rc_key)
|
|
229
|
+
width = plt.rcParams.get(cast(Any, rc_key))
|
|
230
230
|
if width is not None:
|
|
231
231
|
return float(width)
|
|
232
232
|
except Exception:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Optional
|
|
5
|
+
from typing import Any, Optional, cast
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import matplotlib.patches as mpatches # type: ignore[import]
|
|
@@ -116,7 +116,7 @@ def _color_of(artist):
|
|
|
116
116
|
return color[0]
|
|
117
117
|
if color is not None:
|
|
118
118
|
try:
|
|
119
|
-
return to_hex(color)
|
|
119
|
+
return to_hex(cast(Any, color))
|
|
120
120
|
except Exception:
|
|
121
121
|
return color
|
|
122
122
|
if hasattr(artist, "get_facecolors"):
|