batplot 1.8.41__tar.gz → 1.8.43__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.
Files changed (125) hide show
  1. batplot-1.8.43/DEVELOPING.md +467 -0
  2. {batplot-1.8.41 → batplot-1.8.43}/MANIFEST.in +1 -1
  3. {batplot-1.8.41/batplot.egg-info → batplot-1.8.43}/PKG-INFO +2 -1
  4. {batplot-1.8.41 → batplot-1.8.43}/README.md +1 -0
  5. {batplot-1.8.41 → batplot-1.8.43}/batplot/__init__.py +1 -1
  6. {batplot-1.8.41 → batplot-1.8.43}/batplot/color_utils.py +72 -0
  7. {batplot-1.8.41 → batplot-1.8.43}/batplot/data/CHANGELOG.md +8 -0
  8. {batplot-1.8.41 → batplot-1.8.43}/batplot/ec_common.py +7 -4
  9. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/routing.py +4 -4
  10. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/actions.py +10 -0
  11. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/dqdv_2d.py +5 -2
  12. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/interactive.py +23 -1
  13. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/style.py +4 -0
  14. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/actions.py +6 -0
  15. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/style.py +1 -1
  16. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/cif.py +2 -1
  17. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/colors.py +3 -24
  18. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/interactive.py +54 -14
  19. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/style.py +118 -135
  20. {batplot-1.8.41 → batplot-1.8.43}/batplot/session.py +61 -0
  21. {batplot-1.8.41 → batplot-1.8.43}/batplot/version_check.py +1 -2
  22. {batplot-1.8.41 → batplot-1.8.43/batplot.egg-info}/PKG-INFO +2 -1
  23. {batplot-1.8.41 → batplot-1.8.43}/batplot.egg-info/SOURCES.txt +2 -0
  24. {batplot-1.8.41 → batplot-1.8.43}/pyproject.toml +1 -1
  25. batplot-1.8.43/tests/test_color_utils.py +31 -0
  26. {batplot-1.8.41 → batplot-1.8.43}/tests/test_contracts.py +10 -13
  27. {batplot-1.8.41 → batplot-1.8.43}/tests/test_xy_modules.py +152 -2
  28. {batplot-1.8.41 → batplot-1.8.43}/LICENSE +0 -0
  29. {batplot-1.8.41 → batplot-1.8.43}/NOTICE +0 -0
  30. {batplot-1.8.41 → batplot-1.8.43}/batplot/args.py +0 -0
  31. {batplot-1.8.41 → batplot-1.8.43}/batplot/batch.py +0 -0
  32. {batplot-1.8.41 → batplot-1.8.43}/batplot/batplot.py +0 -0
  33. {batplot-1.8.41 → batplot-1.8.43}/batplot/canvas_interactive.py +0 -0
  34. {batplot-1.8.41 → batplot-1.8.43}/batplot/cif.py +0 -0
  35. {batplot-1.8.41 → batplot-1.8.43}/batplot/cli.py +0 -0
  36. {batplot-1.8.41 → batplot-1.8.43}/batplot/config.py +0 -0
  37. {batplot-1.8.41 → batplot-1.8.43}/batplot/converters.py +0 -0
  38. {batplot-1.8.41 → batplot-1.8.43}/batplot/dev_upgrade.py +0 -0
  39. {batplot-1.8.41 → batplot-1.8.43}/batplot/modes.py +0 -0
  40. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/__init__.py +0 -0
  41. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/__init__.py +0 -0
  42. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/axis_state.py +0 -0
  43. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/files.py +0 -0
  44. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/fonts.py +0 -0
  45. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/interactive_state.py +0 -0
  46. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/menu_rendering.py +0 -0
  47. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/menus.py +0 -0
  48. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/palettes.py +0 -0
  49. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/smoothing.py +0 -0
  50. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/sources.py +0 -0
  51. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/spines.py +0 -0
  52. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/terminal.py +0 -0
  53. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/common/title_offsets.py +0 -0
  54. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/__init__.py +0 -0
  55. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/actions.py +0 -0
  56. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/colors.py +0 -0
  57. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/interactive.py +0 -0
  58. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/labels.py +0 -0
  59. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/legend.py +0 -0
  60. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/menu.py +0 -0
  61. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/session.py +0 -0
  62. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/cpc/snapshots.py +0 -0
  63. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/__init__.py +0 -0
  64. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/colors.py +0 -0
  65. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/export.py +0 -0
  66. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/labels.py +0 -0
  67. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/legend.py +0 -0
  68. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/legend_order.py +0 -0
  69. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/line_style.py +0 -0
  70. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/menu.py +0 -0
  71. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/routing.py +0 -0
  72. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/session.py +0 -0
  73. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/electrochem/spine_colors.py +0 -0
  74. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/__init__.py +0 -0
  75. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/colors.py +0 -0
  76. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/grid.py +0 -0
  77. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/interactive.py +0 -0
  78. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/ions_axis.py +0 -0
  79. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/labels.py +0 -0
  80. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/layout.py +0 -0
  81. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/line_style.py +0 -0
  82. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/menu.py +0 -0
  83. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/peaks.py +0 -0
  84. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/plot.py +0 -0
  85. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/routing.py +0 -0
  86. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/session.py +0 -0
  87. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/operando/visibility.py +0 -0
  88. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/session_routing.py +0 -0
  89. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/__init__.py +0 -0
  90. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/actions.py +0 -0
  91. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/arrange.py +0 -0
  92. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/axis_range.py +0 -0
  93. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/data_ops.py +0 -0
  94. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/derivative.py +0 -0
  95. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/game.py +0 -0
  96. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/labels.py +0 -0
  97. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/line_style.py +0 -0
  98. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/menu.py +0 -0
  99. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/peaks.py +0 -0
  100. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/pipeline.py +0 -0
  101. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/session.py +0 -0
  102. {batplot-1.8.41 → batplot-1.8.43}/batplot/plot_modes/xy/smoothing.py +0 -0
  103. {batplot-1.8.41 → batplot-1.8.43}/batplot/plotting.py +0 -0
  104. {batplot-1.8.41 → batplot-1.8.43}/batplot/readers.py +0 -0
  105. {batplot-1.8.41 → batplot-1.8.43}/batplot/showcol.py +0 -0
  106. {batplot-1.8.41 → batplot-1.8.43}/batplot/style.py +0 -0
  107. {batplot-1.8.41 → batplot-1.8.43}/batplot/ui.py +0 -0
  108. {batplot-1.8.41 → batplot-1.8.43}/batplot/utils.py +0 -0
  109. {batplot-1.8.41 → batplot-1.8.43}/batplot.egg-info/dependency_links.txt +0 -0
  110. {batplot-1.8.41 → batplot-1.8.43}/batplot.egg-info/entry_points.txt +0 -0
  111. {batplot-1.8.41 → batplot-1.8.43}/batplot.egg-info/requires.txt +0 -0
  112. {batplot-1.8.41 → batplot-1.8.43}/batplot.egg-info/top_level.txt +0 -0
  113. {batplot-1.8.41 → batplot-1.8.43}/setup.cfg +0 -0
  114. {batplot-1.8.41 → batplot-1.8.43}/setup.py +0 -0
  115. {batplot-1.8.41 → batplot-1.8.43}/tests/test_cli_smoke.py +0 -0
  116. {batplot-1.8.41 → batplot-1.8.43}/tests/test_common_files.py +0 -0
  117. {batplot-1.8.41 → batplot-1.8.43}/tests/test_common_palettes.py +0 -0
  118. {batplot-1.8.41 → batplot-1.8.43}/tests/test_cpc_roundtrip.py +0 -0
  119. {batplot-1.8.41 → batplot-1.8.43}/tests/test_csv_readers.py +0 -0
  120. {batplot-1.8.41 → batplot-1.8.43}/tests/test_dev_upgrade.py +0 -0
  121. {batplot-1.8.41 → batplot-1.8.43}/tests/test_ec_roundtrip.py +0 -0
  122. {batplot-1.8.41 → batplot-1.8.43}/tests/test_interactive_menu_smoke.py +0 -0
  123. {batplot-1.8.41 → batplot-1.8.43}/tests/test_interactive_state.py +0 -0
  124. {batplot-1.8.41 → batplot-1.8.43}/tests/test_operando_roundtrip.py +0 -0
  125. {batplot-1.8.41 → batplot-1.8.43}/tests/test_xy_roundtrip.py +0 -0
@@ -0,0 +1,467 @@
1
+ # Batplot Developer Guide
2
+
3
+ This guide is for contributors who want to fix bugs, add plotting features, or
4
+ extend Batplot with new file readers, commands, modes, styles, sessions, and
5
+ release tooling.
6
+
7
+ Keep changes small and mode-local whenever possible. Batplot has several
8
+ interactive modes that share concepts but not all implementation details, so a
9
+ fix in one mode should not silently change another mode unless that is the
10
+ explicit goal.
11
+
12
+ ## Development Principles
13
+
14
+ - Touch only the code path needed for the feature or fix.
15
+ - Preserve Windows, macOS, and Linux behavior. Avoid shell-specific assumptions
16
+ in Python code and use `pathlib`, `os.path`, `tempfile`, and structured APIs.
17
+ - Prefer existing mode patterns over new abstractions. Add shared helpers only
18
+ when two or more active modes genuinely need the same behavior.
19
+ - Keep generated, archived, and release-copy folders out of normal edits. The
20
+ active source tree is `batplot/`; do not edit `archive_unused/`, `dist/`,
21
+ `build/`, `*.egg-info/`, or copied release folders such as `batplot-1.8.42/`.
22
+ - If a change fixes a bug, add an entry to `BUGFIXES.md`.
23
+ - If a change affects commands, persistence, style files, packaging, or release
24
+ behavior, update this guide in the same pull request or commit.
25
+
26
+ ## Repository Map
27
+
28
+ ```text
29
+ batplot_script/
30
+ +-- batplot/ # Active package source
31
+ | +-- cli.py # Console entry point wrapper
32
+ | +-- batplot.py # Main dispatcher and legacy compatibility exports
33
+ | +-- args.py # CLI parser and mode flags
34
+ | +-- readers.py # Text, CSV, Excel, EC, and vendor file readers
35
+ | +-- session.py # Shared session save/load implementations
36
+ | +-- plotting.py # Shared plotting utilities
37
+ | +-- ui.py # Canvas/frame/tick/label utilities
38
+ | +-- ec_common.py # Shared GC/CV/dQ/dV/CPC defaults and helpers
39
+ | +-- plot_modes/
40
+ | +-- xy/ # 1D/XRD/PDF/XAS/general XY mode
41
+ | +-- electrochem/ # GC, CV, dQ/dV routes and interactive menu
42
+ | +-- cpc/ # Capacity/energy per cycle mode
43
+ | +-- operando/ # Operando contour + EC mode
44
+ | +-- common/ # Shared menu, spine, palette, and state helpers
45
+ +-- tests/ # Pytest and type-check contract tests
46
+ +-- README.md # User-facing overview
47
+ +-- BUGFIXES.md # Required bug-fix notes
48
+ +-- CHANGELOG.md # Release changelog
49
+ +-- RELEASE_NOTES.txt # Source notes used by --dev-upgrade
50
+ +-- pyproject.toml # Packaging metadata, dependencies, test config
51
+ +-- MANIFEST.in # Source distribution include/exclude rules
52
+ +-- .github/workflows/tests.yml # Cross-platform CI
53
+ ```
54
+
55
+ ## CLI And Dispatch Flow
56
+
57
+ The installed command is declared in `pyproject.toml`:
58
+
59
+ ```toml
60
+ batplot = "batplot.cli:main"
61
+ ```
62
+
63
+ The runtime flow is:
64
+
65
+ 1. `batplot.cli.main()` handles wrapper-only behavior such as `--dev-upgrade`,
66
+ version checking, and test injection of `argv`.
67
+ 2. `batplot.args.parse_args()` builds and validates command-line options.
68
+ 3. `batplot.batplot.batplot_main()` dispatches to the appropriate plotting
69
+ route.
70
+ 4. Mode route functions build the initial figure, then optionally enter the
71
+ mode-specific interactive menu.
72
+
73
+ The main modes are routed through:
74
+
75
+ - XY: `batplot.plot_modes.xy.pipeline.run_xy_pipeline()`
76
+ - GC: `batplot.plot_modes.electrochem.routing.handle_gc_mode()`
77
+ - CV: `batplot.plot_modes.electrochem.routing.handle_cv_mode()`
78
+ - dQ/dV: `batplot.plot_modes.electrochem.routing.handle_dqdv_mode()`
79
+ - CPC/EPC: `batplot.plot_modes.cpc.routing.handle_cpc_mode()`
80
+ - Operando: `batplot.plot_modes.operando.routing.handle_operando_mode()`
81
+ - Saved sessions: `batplot.plot_modes.session_routing.handle_session_reload()`
82
+
83
+ When adding a command-line feature, update `args.py`, then wire the behavior in
84
+ the smallest relevant route or interactive action. Avoid adding mode-specific
85
+ logic directly to `cli.py`; it should remain a thin entry point.
86
+
87
+ ## Plot Mode Structure
88
+
89
+ Most active modes follow the same module pattern:
90
+
91
+ - `routing.py`: non-interactive plotting and setup before interactive mode.
92
+ - `interactive.py`: command loop and key dispatch.
93
+ - `menu.py`: text shown to users in interactive mode.
94
+ - `actions.py`: save/export/undo or other larger command handlers.
95
+ - `style.py`: style export/import helpers when the mode supports style files.
96
+ - `session.py`: public wrappers around shared session implementations.
97
+ - Additional focused modules for labels, colors, line styles, legends, ranges,
98
+ smoothing, peaks, layout, or visibility.
99
+
100
+ ### XY Mode
101
+
102
+ Use `batplot/plot_modes/xy/` for general 1D data such as XRD, PDF, XAS, and
103
+ custom XY text files.
104
+
105
+ Important files:
106
+
107
+ - `pipeline.py`: reads input files, applies conversions, creates the plot.
108
+ - `interactive.py`: user command loop.
109
+ - `actions.py`: style export/import, figure export, session save, undo.
110
+ - `style.py`: `.bps`, `.bpsg`, and `.bpcfg` style and geometry helpers.
111
+ - `session.py`: public XY session wrappers.
112
+ - `colors.py`, `line_style.py`, `labels.py`, `axis_range.py`, `smoothing.py`,
113
+ `derivative.py`, `peaks.py`, `cif.py`, `data_ops.py`, `arrange.py`: focused
114
+ XY features.
115
+
116
+ ### Electrochemistry Mode
117
+
118
+ Use `batplot/plot_modes/electrochem/` for GC, CV, and dQ/dV.
119
+
120
+ Important files:
121
+
122
+ - `routing.py`: `handle_gc_mode()`, `handle_cv_mode()`, `handle_dqdv_mode()`.
123
+ - `interactive.py`: shared EC interactive command loop.
124
+ - `actions.py`: EC style/session/export/undo commands.
125
+ - `dqdv_2d.py`: dQ/dV 2D companion contour persistence and helpers.
126
+ - `style.py`, `colors.py`, `line_style.py`, `labels.py`, `legend.py`,
127
+ `legend_order.py`, `spine_colors.py`: EC styling features.
128
+
129
+ Shared EC/CPC defaults live in `batplot/ec_common.py`. Canvas size, plot layout,
130
+ and mass-resolution behavior should be changed there when the goal is parity
131
+ between GC, CV, dQ/dV, and CPC.
132
+
133
+ ### CPC And EPC Mode
134
+
135
+ Use `batplot/plot_modes/cpc/` for capacity per cycle and energy per cycle.
136
+
137
+ Important files:
138
+
139
+ - `routing.py`: reads EC files, computes charge/discharge/efficiency series,
140
+ and creates the default CPC/EPC figure.
141
+ - `interactive.py`: CPC-specific interactive command loop.
142
+ - `actions.py`: save/export/undo actions.
143
+ - `legend.py`, `colors.py`, `labels.py`, `snapshots.py`: CPC-specific behavior.
144
+ - `session.py`: public CPC session wrappers.
145
+
146
+ CPC shares several expectations with EC but has a second y-axis and scatter
147
+ series for charge, discharge, and efficiency. Test both axes whenever changing
148
+ style, legend, or color behavior.
149
+
150
+ ### Operando Mode
151
+
152
+ Use `batplot/plot_modes/operando/` for contour plots and coupled EC/operando
153
+ interactive workflows.
154
+
155
+ Important files:
156
+
157
+ - `routing.py`: command-line route.
158
+ - `plot.py`: plot construction from folders and data grids.
159
+ - `interactive.py`: combined operando/EC interactive menu.
160
+ - `actions.py`: save/export/undo actions.
161
+ - `layout.py`, `grid.py`, `ions_axis.py`, `visibility.py`: contour layout and
162
+ display behavior.
163
+ - `style.py`, `colors.py`, `labels.py`, `line_style.py`, `peaks.py`: styling
164
+ and annotation behavior.
165
+ - `session.py`: public operando session wrappers.
166
+
167
+ Operando state has more moving parts than the other modes. Keep changes focused
168
+ and add round-trip tests for persistence changes.
169
+
170
+ ## Shared Systems
171
+
172
+ ### Readers
173
+
174
+ File parsing lives primarily in `batplot/readers.py`.
175
+
176
+ Add a reader here when:
177
+
178
+ - A new file format is needed by more than one mode.
179
+ - Parsing requires format-specific handling.
180
+ - The data needs robust header, delimiter, encoding, or mixed text/numeric
181
+ behavior.
182
+
183
+ For XY formats, also check `batplot/plot_modes/xy/pipeline.py` and any extension
184
+ lists in `batplot/batplot.py`. For electrochemistry formats, wire the reader in
185
+ `electrochem/routing.py` or `cpc/routing.py`.
186
+
187
+ Reader guidelines:
188
+
189
+ - Use `encoding="utf-8"` when writing text; handle common read encodings only
190
+ where necessary.
191
+ - Avoid platform-specific paths or separators.
192
+ - Return plain Python/numpy structures that routes can validate.
193
+ - Add focused tests with tiny fixture files or temporary files.
194
+
195
+ ### Style Files
196
+
197
+ Interactive style export/import uses `.bps`, `.bpsg`, and `.bpcfg` files.
198
+
199
+ The important rule is p/i parity:
200
+
201
+ - `p`: export/print/save style or style+geometry.
202
+ - `i`: import the same fields back.
203
+
204
+ When adding a style field, update all relevant export and import code in the
205
+ same mode. For geometry-affecting fields, verify style+geometry files as well as
206
+ style-only files.
207
+
208
+ Mode locations:
209
+
210
+ - XY: `batplot/plot_modes/xy/style.py` and `xy/actions.py`
211
+ - EC: `batplot/plot_modes/electrochem/style.py` and `electrochem/actions.py`
212
+ - CPC: `batplot/plot_modes/cpc/actions.py` and related helpers
213
+ - Operando: `batplot/plot_modes/operando/style.py` and `operando/actions.py`
214
+
215
+ ### Sessions
216
+
217
+ Sessions are `.pkl` files that preserve a full plot state. Public mode modules
218
+ mostly re-export implementations from `batplot/session.py`.
219
+
220
+ The important rule is s/b parity:
221
+
222
+ - `s`: save enough state to reproduce the figure later.
223
+ - `b`: undo must restore the same user-visible state when applicable.
224
+
225
+ When adding a persistent field:
226
+
227
+ 1. Add it to the relevant dump function in `batplot/session.py` or the
228
+ mode-local state builder.
229
+ 2. Load it in the corresponding load function.
230
+ 3. Add it to undo snapshots if users can change it interactively.
231
+ 4. Keep backward compatibility with old session files by using safe defaults for
232
+ missing keys.
233
+ 5. Add or update a round-trip test.
234
+
235
+ Saved-session dispatch is in `batplot/plot_modes/session_routing.py`. New
236
+ session kinds must be routed there.
237
+
238
+ ### Undo
239
+
240
+ Undo is mode-specific and should restore the state a user just changed. When
241
+ adding an interactive command that mutates the figure:
242
+
243
+ - Capture state before mutation.
244
+ - Restore artists, axes, labels, colors, legends, limits, visibility, and helper
245
+ attributes touched by the command.
246
+ - Redraw the canvas after restore where the mode already does so.
247
+
248
+ Do not add broad undo state if only one artist needs tracking.
249
+
250
+ ### Menus And Interactive Commands
251
+
252
+ Interactive command additions usually require three edits:
253
+
254
+ 1. Add the displayed command to the mode's `menu.py`.
255
+ 2. Dispatch the command in the mode's `interactive.py`.
256
+ 3. Put larger behavior in `actions.py` or a focused helper module.
257
+
258
+ Common command conventions:
259
+
260
+ - `p`: export style or style+geometry.
261
+ - `i`: import style or style+geometry.
262
+ - `e`: export figure.
263
+ - `s`: save session.
264
+ - `b`: undo.
265
+ - `q`: quit.
266
+ - `oe`, `os`, `ops`, `opsg`: overwrite previous export/session/style paths
267
+ where supported.
268
+
269
+ Keep command syntax consistent across XY, EC, CPC, and operando when adding a
270
+ shared concept.
271
+
272
+ ### Colors And Palettes
273
+
274
+ Use shared color helpers when possible:
275
+
276
+ - `batplot/color_utils.py`: colormap lookup and terminal color helpers.
277
+ - `batplot/plot_modes/common/palettes.py`: palette aliases, range parsing, and
278
+ sampling.
279
+ - `batplot/plotting.py`: shared artist helpers such as color application.
280
+
281
+ When a line can be shown as markers only, make sure both line color and marker
282
+ face/edge colors are updated. Legends and plotted artists should not diverge.
283
+
284
+ ### Axes, Canvas, And Layout
285
+
286
+ Use `batplot/ui.py` for canvas, frame, tick, and label positioning helpers.
287
+ Use `batplot/plot_modes/common/spines.py` for WASD spine/tick/title state.
288
+
289
+ Shared GC/CV/dQ/dV/CPC defaults live in `batplot/ec_common.py`:
290
+
291
+ - `_EC_DEFAULT_FIGSIZE`
292
+ - `_EC_DEFAULT_LAYOUT`
293
+ - `_CPC_DEFAULT_LAYOUT`
294
+ - `_default_ec_figsize()`
295
+ - `_default_cpc_figsize()`
296
+ - `_apply_default_ec_layout()`
297
+
298
+ Do not hardcode replacement figure sizes in individual EC/CPC routes unless a
299
+ mode is intentionally different and tests document the difference.
300
+
301
+ ## How To Add Common Features
302
+
303
+ ### Add A New CLI Flag
304
+
305
+ 1. Add the option in `batplot/args.py`.
306
+ 2. Decide whether it is global or mode-specific.
307
+ 3. Read the value in the smallest route that needs it.
308
+ 4. Add CLI smoke tests when the flag affects dispatch.
309
+ 5. Update `README.md` or user-facing help if the flag is public.
310
+
311
+ ### Add A New File Reader
312
+
313
+ 1. Implement parsing in `batplot/readers.py`.
314
+ 2. Add a tiny representative test file or temporary-file test.
315
+ 3. Wire the reader into the relevant route:
316
+ - XY: `batplot/plot_modes/xy/pipeline.py`
317
+ - GC/CV/dQ/dV: `batplot/plot_modes/electrochem/routing.py`
318
+ - CPC/EPC: `batplot/plot_modes/cpc/routing.py`
319
+ - Operando: `batplot/plot_modes/operando/plot.py` or `routing.py`
320
+ 4. Update extension handling if `--all` or `--showcol` should recognize it.
321
+ 5. Confirm behavior on paths with spaces.
322
+
323
+ ### Add A New Interactive Command
324
+
325
+ 1. Choose a command syntax that does not conflict with existing keys.
326
+ 2. Add menu text in the mode's `menu.py`.
327
+ 3. Add parsing/dispatch in `interactive.py`.
328
+ 4. Put non-trivial logic in `actions.py` or a focused module.
329
+ 5. Add undo support if it mutates the plot.
330
+ 6. Add style/session support if the result should persist.
331
+ 7. Add an interactive smoke test or a focused unit test for the helper.
332
+
333
+ ### Add A New Style Field
334
+
335
+ 1. Find the mode's style export and import code.
336
+ 2. Export the field under a clear key.
337
+ 3. Import the key with a safe default for older style files.
338
+ 4. Add the field to geometry export/import if it affects layout.
339
+ 5. Add or update a round-trip test.
340
+
341
+ Checklist:
342
+
343
+ - `p` exports it.
344
+ - `i` imports it.
345
+ - `s` saves it if it should persist in sessions.
346
+ - `b` undoes it if it can be changed interactively.
347
+
348
+ ### Add A New Session Field
349
+
350
+ 1. Add the field to the relevant dump function in `batplot/session.py`.
351
+ 2. Add load logic in the matching load function.
352
+ 3. Use defaults for missing legacy keys.
353
+ 4. Add undo state if the field can be changed from the interactive menu.
354
+ 5. Add a round-trip test in the relevant `tests/test_*_roundtrip.py` file.
355
+
356
+ ### Add A New Plot Mode
357
+
358
+ 1. Create `batplot/plot_modes/<mode>/`.
359
+ 2. Start with `routing.py`, `interactive.py`, `menu.py`, `actions.py`, and
360
+ `session.py` if persistence is needed.
361
+ 3. Add CLI flags in `batplot/args.py`.
362
+ 4. Dispatch in `batplot/batplot.py`.
363
+ 5. Add session reload support in `batplot/plot_modes/session_routing.py` if the
364
+ mode saves `.pkl` sessions.
365
+ 6. Add tests for CLI dispatch, output creation, style/session round trips, and
366
+ any readers.
367
+ 7. Update `README.md`, `CHANGELOG.md` or `RELEASE_NOTES.txt` when user-facing.
368
+
369
+ Prefer extracting shared helpers only after the new mode proves it needs the
370
+ same behavior as an existing active mode.
371
+
372
+ ## Testing
373
+
374
+ Install development dependencies:
375
+
376
+ ```bash
377
+ python -m pip install -e ".[test]"
378
+ ```
379
+
380
+ Run the full test suite:
381
+
382
+ ```bash
383
+ python -m pytest tests/ -q
384
+ ```
385
+
386
+ Run the type checker:
387
+
388
+ ```bash
389
+ python -m basedpyright .
390
+ ```
391
+
392
+ Useful focused tests:
393
+
394
+ - CLI behavior: `python -m pytest tests/test_cli_smoke.py -q`
395
+ - Public contracts: `python -m pytest tests/test_contracts.py -q`
396
+ - XY round trips: `python -m pytest tests/test_xy_roundtrip.py -q`
397
+ - EC round trips: `python -m pytest tests/test_ec_roundtrip.py -q`
398
+ - CPC round trips: `python -m pytest tests/test_cpc_roundtrip.py -q`
399
+ - Operando round trips: `python -m pytest tests/test_operando_roundtrip.py -q`
400
+ - Readers: `python -m pytest tests/test_csv_readers.py -q`
401
+ - Release tooling: `python -m pytest tests/test_dev_upgrade.py -q`
402
+
403
+ For fixes that touch plotting, prefer at least one focused unit or round-trip
404
+ test plus one smoke test for the user path. For small documentation-only edits,
405
+ no test run is required.
406
+
407
+ ## Packaging And Release Workflow
408
+
409
+ Packaging metadata is in:
410
+
411
+ - `pyproject.toml`
412
+ - `setup.py`
413
+ - `MANIFEST.in`
414
+ - `CITATION.cff`
415
+ - `batplot/__init__.py`
416
+ - `batplot/version_check.py`
417
+
418
+ Release notes are maintained in:
419
+
420
+ - `RELEASE_NOTES.txt`
421
+ - `CHANGELOG.md`
422
+ - `batplot/data/CHANGELOG.md`
423
+
424
+ The `batplot --dev-upgrade` command is implemented in `batplot/dev_upgrade.py`.
425
+ It reads release notes, updates version-related files, validates distribution
426
+ contents, builds, uploads, and stages release changes.
427
+
428
+ Before changing release behavior:
429
+
430
+ 1. Read `batplot/dev_upgrade.py`.
431
+ 2. Add or update tests in `tests/test_dev_upgrade.py`.
432
+ 3. Confirm future files are included through package discovery or manifest rules.
433
+ 4. Keep GitHub/PyPI conflict handling explicit and testable.
434
+
435
+ ## Cross-Platform Checklist
436
+
437
+ Use this checklist before finishing code changes:
438
+
439
+ - Paths with spaces work.
440
+ - No hardcoded `/` or `\` path assumptions in Python logic.
441
+ - Temporary files use `tempfile` or pytest `tmp_path`.
442
+ - Text files specify encoding when written.
443
+ - No dependency on an interactive terminal unless the command is explicitly
444
+ interactive.
445
+ - Matplotlib code does not require a GUI backend for tests.
446
+ - Tests pass on case-sensitive and case-insensitive filesystems.
447
+ - New dependencies are declared in `pyproject.toml` and are OS independent.
448
+
449
+ ## Documentation Maintenance
450
+
451
+ Update this guide whenever a change adds, removes, or significantly changes:
452
+
453
+ - A CLI flag or mode dispatch path.
454
+ - A plot mode package or module responsibility.
455
+ - A file reader or supported input format.
456
+ - Interactive command syntax.
457
+ - Style/session/undo behavior.
458
+ - Packaging, release, or `--dev-upgrade` behavior.
459
+ - Required test commands or CI expectations.
460
+
461
+ For bug fixes, also update `BUGFIXES.md`. For user-visible features, update
462
+ `README.md`, `CHANGELOG.md`, or `RELEASE_NOTES.txt` as appropriate.
463
+
464
+ The goal is for a future contributor to answer two questions quickly:
465
+
466
+ 1. Where should this change be made?
467
+ 2. What else must be updated so p/i/s/b, tests, docs, and releases stay in sync?
@@ -1,3 +1,3 @@
1
- include README.md LICENSE
1
+ include README.md LICENSE DEVELOPING.md
2
2
  recursive-include batplot/data CHANGELOG.md
3
3
  exclude batplot/data/USER_MANUAL.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.41
3
+ Version: 1.8.43
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
@@ -368,6 +368,7 @@ batplot --m # Open illustrated manual
368
368
 
369
369
  - [USER_MANUAL.md](USER_MANUAL.md) — Detailed usage and workflows
370
370
  - [FLAGS_REFERENCE.md](FLAGS_REFERENCE.md) — Complete flag reference by mode
371
+ - [DEVELOPING.md](DEVELOPING.md) — Developer guide for extending Batplot
371
372
 
372
373
  ---
373
374
 
@@ -314,6 +314,7 @@ batplot --m # Open illustrated manual
314
314
 
315
315
  - [USER_MANUAL.md](USER_MANUAL.md) — Detailed usage and workflows
316
316
  - [FLAGS_REFERENCE.md](FLAGS_REFERENCE.md) — Complete flag reference by mode
317
+ - [DEVELOPING.md](DEVELOPING.md) — Developer guide for extending Batplot
317
318
 
318
319
  ---
319
320
 
@@ -7,7 +7,7 @@ import importlib.abc
7
7
  import importlib.machinery
8
8
  import sys
9
9
 
10
- __version__ = "1.8.41"
10
+ __version__ = "1.8.43"
11
11
 
12
12
 
13
13
  _LEGACY_MODULE_ALIASES = {
@@ -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',
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.43] - 2026-06-25
4
+ - Bug fixes
5
+
6
+
7
+ ## [1.8.42] - 2026-06-12
8
+ - Bug fixes
9
+
10
+
3
11
  ## [1.8.41] - 2026-06-12
4
12
  - Bug fixes
5
13
  - Complete update of the codebase
@@ -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
- _CPC_DEFAULT_LAYOUT = {'left': 0.12, 'right': 0.88, 'top': 0.88, 'bottom': 0.15}
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
- return _figsize_for_frame(_CPC_DEFAULT_LAYOUT)
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 while keeping the plotted frame size identical."""
47
- fig.subplots_adjust(**(_CPC_DEFAULT_LAYOUT if cpc else _EC_DEFAULT_LAYOUT))
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
- _default_cpc_figsize,
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 figsize as GC)
344
- fig, ax = plt.subplots(figsize=_default_cpc_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, cpc=True)
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