batplot 1.8.4__py3-none-any.whl → 1.8.6__py3-none-any.whl
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/__init__.py +1 -1
- batplot/args.py +22 -4
- batplot/batch.py +12 -0
- batplot/batplot.py +340 -126
- batplot/converters.py +170 -122
- batplot/cpc_interactive.py +319 -161
- batplot/data/USER_MANUAL.md +49 -0
- batplot/electrochem_interactive.py +120 -80
- batplot/interactive.py +1763 -75
- batplot/modes.py +12 -11
- batplot/operando.py +22 -0
- batplot/operando_ec_interactive.py +390 -16
- batplot/session.py +85 -9
- batplot/style.py +198 -21
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/METADATA +1 -1
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/RECORD +20 -20
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/WHEEL +1 -1
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/entry_points.txt +0 -0
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/top_level.txt +0 -0
batplot/data/USER_MANUAL.md
CHANGED
|
@@ -167,6 +167,55 @@ batplot data.xye:0.25:1.54 --xaxis 2theta --interactive
|
|
|
167
167
|
# Multiple files with different wavelengths
|
|
168
168
|
batplot file1.xye:1.5406 file2.xye:0.7093 pattern.cif:1.5406 --xaxis 2theta
|
|
169
169
|
# Each file uses its own wavelength; CIF ticks use 1.5406 Å
|
|
170
|
+
|
|
171
|
+
## Data Conversion (`--convert`)
|
|
172
|
+
|
|
173
|
+
The `--convert` flag allows you to convert XRD data files between different representations and export them to a new `converted` subfolder. This is useful for batch conversion of files without opening them in the interactive plotter.
|
|
174
|
+
|
|
175
|
+
### Conversion Modes
|
|
176
|
+
|
|
177
|
+
1. **Wavelength-to-wavelength conversion**:
|
|
178
|
+
- Convert 2θ values from one wavelength to another
|
|
179
|
+
- Syntax: `batplot file.xye --convert <wavelength1> <wavelength2>`
|
|
180
|
+
- Example: `batplot file.xye --convert 1.54 0.25`
|
|
181
|
+
- Converts 2θ values measured with λ=1.54 Å to equivalent 2θ values for λ=0.25 Å
|
|
182
|
+
- Process: 2θ(λ=1.54) → Q → 2θ(λ=0.25)
|
|
183
|
+
|
|
184
|
+
2. **Wavelength-to-Q conversion**:
|
|
185
|
+
- Convert 2θ values (with given wavelength) to Q space
|
|
186
|
+
- Syntax: `batplot file.xye --convert <wavelength> q`
|
|
187
|
+
- Example: `batplot file.xye --convert 1.54 q`
|
|
188
|
+
- Converts 2θ values measured with λ=1.54 Å to Q space
|
|
189
|
+
- Output file: `converted/file.qye`
|
|
190
|
+
|
|
191
|
+
3. **Q-to-wavelength conversion**:
|
|
192
|
+
- Convert Q space values to 2θ (with given wavelength)
|
|
193
|
+
- Syntax: `batplot file.qye --convert q <wavelength>`
|
|
194
|
+
- Example: `batplot file.qye --convert q 1.54`
|
|
195
|
+
- Converts Q values to 2θ values for λ=1.54 Å
|
|
196
|
+
- Output file: `converted/file.xy`
|
|
197
|
+
|
|
198
|
+
### Output Location
|
|
199
|
+
|
|
200
|
+
All converted files are saved in a `converted` subfolder within the directory containing the input files. The original file names are preserved, but extensions may change:
|
|
201
|
+
- Q-space files: `.qye` extension
|
|
202
|
+
- 2θ files: `.xy` or original extension
|
|
203
|
+
|
|
204
|
+
### Examples
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Convert 2θ from Cu Kα (1.54 Å) to Mo Kα (0.709 Å)
|
|
208
|
+
batplot pattern.xye --convert 1.54 0.709
|
|
209
|
+
|
|
210
|
+
# Convert 2θ to Q space
|
|
211
|
+
batplot pattern.xye --convert 1.54 q
|
|
212
|
+
|
|
213
|
+
# Convert Q to 2θ
|
|
214
|
+
batplot pattern.qye --convert q 1.54
|
|
215
|
+
|
|
216
|
+
# Convert multiple files
|
|
217
|
+
batplot file1.xye file2.xye --convert 1.54 0.25
|
|
218
|
+
```
|
|
170
219
|
```
|
|
171
220
|
|
|
172
221
|
**Note:** When using dual wavelength conversion, the crosshair (press `n` in interactive mode) will automatically display both the original 2theta (calculated from λ₁) and the current 2theta (displayed axis, calculated from λ₂), along with Q and d-spacing values.
|
|
@@ -374,7 +374,7 @@ def _apply_stored_smooth_settings(cycle_lines: Dict[int, Dict[str, Optional[obje
|
|
|
374
374
|
ln._smooth_applied = True
|
|
375
375
|
|
|
376
376
|
|
|
377
|
-
def _print_menu(n_cycles: int, is_dqdv: bool = False):
|
|
377
|
+
def _print_menu(n_cycles: int, is_dqdv: bool = False, fig=None):
|
|
378
378
|
# Three-column menu similar to operando: Styles | Geometries | Options
|
|
379
379
|
# Use dynamic column widths for clean alignment.
|
|
380
380
|
col1 = [
|
|
@@ -405,6 +405,19 @@ def _print_menu(n_cycles: int, is_dqdv: bool = False):
|
|
|
405
405
|
"b: undo",
|
|
406
406
|
"q: quit",
|
|
407
407
|
]
|
|
408
|
+
|
|
409
|
+
# Conditional overwrite shortcuts under (Options)
|
|
410
|
+
if fig is not None:
|
|
411
|
+
last_session = getattr(fig, "_last_session_save_path", None)
|
|
412
|
+
last_style = getattr(fig, "_last_style_export_path", None)
|
|
413
|
+
last_figure = getattr(fig, "_last_figure_export_path", None)
|
|
414
|
+
if last_session:
|
|
415
|
+
col3.append("os: overwrite session")
|
|
416
|
+
if last_style:
|
|
417
|
+
col3.append("ops: overwrite style")
|
|
418
|
+
col3.append("opsg: overwrite style+geom")
|
|
419
|
+
if last_figure:
|
|
420
|
+
col3.append("oe: overwrite figure")
|
|
408
421
|
# Compute widths (min width prevents overly narrow columns)
|
|
409
422
|
w1 = max(len("(Styles)"), *(len(s) for s in col1), 18)
|
|
410
423
|
w2 = max(len("(Geometries)"), *(len(s) for s in col2), 12)
|
|
@@ -1640,7 +1653,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1640
1653
|
print("Undo: restored previous state.")
|
|
1641
1654
|
except Exception as e:
|
|
1642
1655
|
print(f"Undo failed: {e}")
|
|
1643
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1656
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1644
1657
|
while True:
|
|
1645
1658
|
try:
|
|
1646
1659
|
key = _safe_input("Press a key: ").strip().lower()
|
|
@@ -1657,18 +1670,18 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1657
1670
|
if confirm == 'y':
|
|
1658
1671
|
break
|
|
1659
1672
|
else:
|
|
1660
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1673
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1661
1674
|
continue
|
|
1662
1675
|
elif key == 'b':
|
|
1663
1676
|
restore_state()
|
|
1664
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1677
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1665
1678
|
continue
|
|
1666
1679
|
elif key == 'e':
|
|
1667
1680
|
# Export current figure to a file; default extension .svg if missing
|
|
1668
1681
|
try:
|
|
1669
1682
|
base_path = choose_save_path(source_paths, purpose="figure export")
|
|
1670
1683
|
if not base_path:
|
|
1671
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1684
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1672
1685
|
continue
|
|
1673
1686
|
# List existing figure files in Figures/ subdirectory
|
|
1674
1687
|
fig_extensions = ('.svg', '.png', '.jpg', '.jpeg', '.pdf', '.eps', '.tif', '.tiff')
|
|
@@ -1690,7 +1703,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1690
1703
|
else:
|
|
1691
1704
|
fname = _safe_input("Export filename (default .svg if no extension) or number to overwrite (q=cancel): ").strip()
|
|
1692
1705
|
if not fname or fname.lower() == 'q':
|
|
1693
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1706
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1694
1707
|
continue
|
|
1695
1708
|
|
|
1696
1709
|
already_confirmed = False # Initialize for new filename case
|
|
@@ -1698,15 +1711,15 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1698
1711
|
if fname.lower() == 'o':
|
|
1699
1712
|
if not last_figure_path:
|
|
1700
1713
|
print("No previous export found.")
|
|
1701
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1714
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1702
1715
|
continue
|
|
1703
1716
|
if not os.path.exists(last_figure_path):
|
|
1704
1717
|
print(f"Previous export file not found: {last_figure_path}")
|
|
1705
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1718
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1706
1719
|
continue
|
|
1707
1720
|
yn = _safe_input(f"Overwrite '{os.path.basename(last_figure_path)}'? (y/n): ").strip().lower()
|
|
1708
1721
|
if yn != 'y':
|
|
1709
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1722
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1710
1723
|
continue
|
|
1711
1724
|
target = last_figure_path
|
|
1712
1725
|
already_confirmed = True
|
|
@@ -1718,13 +1731,13 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1718
1731
|
name = files[idx-1]
|
|
1719
1732
|
yn = _safe_input(f"Overwrite '{name}'? (y/n): ").strip().lower()
|
|
1720
1733
|
if yn != 'y':
|
|
1721
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1734
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1722
1735
|
continue
|
|
1723
1736
|
target = file_list[idx-1][1] # Full path from list
|
|
1724
1737
|
already_confirmed = True
|
|
1725
1738
|
else:
|
|
1726
1739
|
print("Invalid number.")
|
|
1727
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1740
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1728
1741
|
continue
|
|
1729
1742
|
else:
|
|
1730
1743
|
root, ext = os.path.splitext(fname)
|
|
@@ -1807,7 +1820,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1807
1820
|
print(f"Export failed: {e}")
|
|
1808
1821
|
except Exception as e:
|
|
1809
1822
|
print(f"Export failed: {e}")
|
|
1810
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
1823
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1811
1824
|
continue
|
|
1812
1825
|
elif key == 'h':
|
|
1813
1826
|
# Legend submenu: toggle visibility and move legend in inches relative to canvas center
|
|
@@ -1896,18 +1909,27 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1896
1909
|
continue
|
|
1897
1910
|
push_state("legend-position")
|
|
1898
1911
|
try:
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1912
|
+
# Store title before updating position
|
|
1913
|
+
_store_legend_title(fig, ax)
|
|
1914
|
+
# Sanitize and store the new position
|
|
1915
|
+
new_pos = _sanitize_legend_offset(fig, (x_in, xy_in[1]))
|
|
1916
|
+
if new_pos is not None:
|
|
1917
|
+
fig._ec_legend_xy_in = new_pos
|
|
1918
|
+
# If legend visible, reposition now
|
|
1919
|
+
leg = ax.get_legend()
|
|
1920
|
+
if leg is not None and leg.get_visible():
|
|
1921
|
+
if not _apply_legend_position(fig, ax):
|
|
1922
|
+
# Fallback: rebuild with title preserved
|
|
1923
|
+
handles, labels = _visible_legend_entries(ax)
|
|
1924
|
+
if handles:
|
|
1925
|
+
legend_title = _get_legend_title(fig)
|
|
1926
|
+
_legend_no_frame(ax, handles, labels, loc='best', borderaxespad=1.0, title=legend_title)
|
|
1927
|
+
fig.canvas.draw_idle()
|
|
1928
|
+
print(f"Legend position updated: x={new_pos[0]:.2f}, y={new_pos[1]:.2f}")
|
|
1929
|
+
else:
|
|
1930
|
+
print(f"Invalid position: x={x_in:.2f} is out of bounds. Position not updated.")
|
|
1931
|
+
except Exception as e:
|
|
1932
|
+
print(f"Error updating legend position: {e}")
|
|
1911
1933
|
elif pos_cmd == 'y':
|
|
1912
1934
|
# Y only: stay in loop
|
|
1913
1935
|
while True:
|
|
@@ -1923,18 +1945,27 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1923
1945
|
continue
|
|
1924
1946
|
push_state("legend-position")
|
|
1925
1947
|
try:
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1948
|
+
# Store title before updating position
|
|
1949
|
+
_store_legend_title(fig, ax)
|
|
1950
|
+
# Sanitize and store the new position
|
|
1951
|
+
new_pos = _sanitize_legend_offset(fig, (xy_in[0], y_in))
|
|
1952
|
+
if new_pos is not None:
|
|
1953
|
+
fig._ec_legend_xy_in = new_pos
|
|
1954
|
+
# If legend visible, reposition now
|
|
1955
|
+
leg = ax.get_legend()
|
|
1956
|
+
if leg is not None and leg.get_visible():
|
|
1957
|
+
if not _apply_legend_position(fig, ax):
|
|
1958
|
+
# Fallback: rebuild with title preserved
|
|
1959
|
+
handles, labels = _visible_legend_entries(ax)
|
|
1960
|
+
if handles:
|
|
1961
|
+
legend_title = _get_legend_title(fig)
|
|
1962
|
+
_legend_no_frame(ax, handles, labels, loc='best', borderaxespad=1.0, title=legend_title)
|
|
1963
|
+
fig.canvas.draw_idle()
|
|
1964
|
+
print(f"Legend position updated: x={new_pos[0]:.2f}, y={new_pos[1]:.2f}")
|
|
1965
|
+
else:
|
|
1966
|
+
print(f"Invalid position: y={y_in:.2f} is out of bounds. Position not updated.")
|
|
1967
|
+
except Exception as e:
|
|
1968
|
+
print(f"Error updating legend position: {e}")
|
|
1938
1969
|
else:
|
|
1939
1970
|
# Try to parse as "x y" format
|
|
1940
1971
|
parts = pos_cmd.replace(',', ' ').split()
|
|
@@ -1946,23 +1977,32 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1946
1977
|
print("Invalid numbers."); continue
|
|
1947
1978
|
push_state("legend-position")
|
|
1948
1979
|
try:
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1980
|
+
# Store title before updating position
|
|
1981
|
+
_store_legend_title(fig, ax)
|
|
1982
|
+
# Sanitize and store the new position
|
|
1983
|
+
new_pos = _sanitize_legend_offset(fig, (x_in, y_in))
|
|
1984
|
+
if new_pos is not None:
|
|
1985
|
+
fig._ec_legend_xy_in = new_pos
|
|
1986
|
+
# If legend visible, reposition now
|
|
1987
|
+
leg = ax.get_legend()
|
|
1988
|
+
if leg is not None and leg.get_visible():
|
|
1989
|
+
if not _apply_legend_position(fig, ax):
|
|
1990
|
+
# Fallback: rebuild with title preserved
|
|
1991
|
+
handles, labels = _visible_legend_entries(ax)
|
|
1992
|
+
if handles:
|
|
1993
|
+
legend_title = _get_legend_title(fig)
|
|
1994
|
+
_legend_no_frame(ax, handles, labels, loc='best', borderaxespad=1.0, title=legend_title)
|
|
1995
|
+
fig.canvas.draw_idle()
|
|
1996
|
+
print(f"Legend position updated: x={new_pos[0]:.2f}, y={new_pos[1]:.2f}")
|
|
1997
|
+
else:
|
|
1998
|
+
print(f"Invalid position: x={x_in:.2f}, y={y_in:.2f} is out of bounds. Position not updated.")
|
|
1999
|
+
except Exception as e:
|
|
2000
|
+
print(f"Error updating legend position: {e}")
|
|
1961
2001
|
else:
|
|
1962
2002
|
print("Unknown option.")
|
|
1963
2003
|
except Exception:
|
|
1964
2004
|
pass
|
|
1965
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2005
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
1966
2006
|
continue
|
|
1967
2007
|
elif key == 'p':
|
|
1968
2008
|
# Print/export style or style+geometry
|
|
@@ -2070,14 +2110,14 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2070
2110
|
print("Unknown choice.")
|
|
2071
2111
|
except Exception as e:
|
|
2072
2112
|
print(f"Error in style submenu: {e}")
|
|
2073
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2113
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2074
2114
|
continue
|
|
2075
2115
|
elif key == 'i':
|
|
2076
2116
|
# Import style from .bps/.bpsg/.bpcfg
|
|
2077
2117
|
try:
|
|
2078
2118
|
path = choose_style_file(source_paths, purpose="style import")
|
|
2079
2119
|
if not path:
|
|
2080
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2120
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2081
2121
|
continue
|
|
2082
2122
|
push_state("import-style")
|
|
2083
2123
|
with open(path, 'r', encoding='utf-8') as f:
|
|
@@ -2087,7 +2127,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2087
2127
|
kind = cfg.get('kind', '')
|
|
2088
2128
|
if kind not in ('ec_style', 'ec_style_geom'):
|
|
2089
2129
|
print("Not an EC style file.")
|
|
2090
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2130
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2091
2131
|
continue
|
|
2092
2132
|
|
|
2093
2133
|
# Enforce compatibility between style/geom ro state and current figure ro state
|
|
@@ -2099,7 +2139,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2099
2139
|
else:
|
|
2100
2140
|
print("Warning: EC style/geometry file was saved without --ro; current plot was created with --ro.")
|
|
2101
2141
|
print("Not applying EC style/geometry to avoid corrupting axis orientation.")
|
|
2102
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2142
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2103
2143
|
continue
|
|
2104
2144
|
|
|
2105
2145
|
has_geometry = (kind == 'ec_style_geom' and 'geometry' in cfg)
|
|
@@ -2436,7 +2476,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2436
2476
|
|
|
2437
2477
|
except Exception as e:
|
|
2438
2478
|
print(f"Error importing style: {e}")
|
|
2439
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2479
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2440
2480
|
continue
|
|
2441
2481
|
elif key == 'l':
|
|
2442
2482
|
# Line widths submenu: curves vs frame/ticks
|
|
@@ -2664,7 +2704,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2664
2704
|
print("Unknown option.")
|
|
2665
2705
|
except Exception as e:
|
|
2666
2706
|
print(f"Error in line submenu: {e}")
|
|
2667
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2707
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2668
2708
|
continue
|
|
2669
2709
|
elif key == 'k':
|
|
2670
2710
|
# Spine colors (w=top, a=left, s=bottom, d=right)
|
|
@@ -2722,7 +2762,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2722
2762
|
fig.canvas.draw()
|
|
2723
2763
|
except Exception as e:
|
|
2724
2764
|
print(f"Error in spine color menu: {e}")
|
|
2725
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2765
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2726
2766
|
continue
|
|
2727
2767
|
elif key == 'r':
|
|
2728
2768
|
# Rename axis labels
|
|
@@ -2792,7 +2832,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2792
2832
|
fig.canvas.draw_idle()
|
|
2793
2833
|
except Exception as e:
|
|
2794
2834
|
print(f"Error renaming axes: {e}")
|
|
2795
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
2835
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2796
2836
|
continue
|
|
2797
2837
|
elif key == 't':
|
|
2798
2838
|
# Unified WASD: w/a/s/d x 1..5 => spine, ticks, minor, labels, title
|
|
@@ -2988,7 +3028,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2988
3028
|
fig.canvas.draw_idle()
|
|
2989
3029
|
except Exception as e:
|
|
2990
3030
|
print(f"Error in WASD tick visibility menu: {e}")
|
|
2991
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3031
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
2992
3032
|
continue
|
|
2993
3033
|
elif key == 's':
|
|
2994
3034
|
try:
|
|
@@ -2996,7 +3036,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2996
3036
|
last_session_path = getattr(fig, '_last_session_save_path', None)
|
|
2997
3037
|
folder = choose_save_path(source_paths, purpose="EC session save")
|
|
2998
3038
|
if not folder:
|
|
2999
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3039
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3000
3040
|
print(f"\nChosen path: {folder}")
|
|
3001
3041
|
try:
|
|
3002
3042
|
files = sorted([f for f in os.listdir(folder) if f.lower().endswith('.pkl')])
|
|
@@ -3017,35 +3057,35 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3017
3057
|
prompt = "Enter new filename (no ext needed) or number to overwrite (q=cancel): "
|
|
3018
3058
|
choice = _safe_input(prompt).strip()
|
|
3019
3059
|
if not choice or choice.lower() == 'q':
|
|
3020
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3060
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3021
3061
|
if choice.lower() == 'o':
|
|
3022
3062
|
# Overwrite last saved session
|
|
3023
3063
|
if not last_session_path:
|
|
3024
3064
|
print("No previous save found.")
|
|
3025
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3065
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3026
3066
|
if not os.path.exists(last_session_path):
|
|
3027
3067
|
print(f"Previous save file not found: {last_session_path}")
|
|
3028
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3068
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3029
3069
|
yn = _safe_input(f"Overwrite '{os.path.basename(last_session_path)}'? (y/n): ").strip().lower()
|
|
3030
3070
|
if yn != 'y':
|
|
3031
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3071
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3032
3072
|
dump_ec_session(last_session_path, fig=fig, ax=ax, cycle_lines=cycle_lines, skip_confirm=True)
|
|
3033
3073
|
print(f"Overwritten session to {last_session_path}")
|
|
3034
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3074
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3035
3075
|
if choice.isdigit() and files:
|
|
3036
3076
|
idx = int(choice)
|
|
3037
3077
|
if 1 <= idx <= len(files):
|
|
3038
3078
|
name = files[idx-1]
|
|
3039
3079
|
yn = _safe_input(f"Overwrite '{name}'? (y/n): ").strip().lower()
|
|
3040
3080
|
if yn != 'y':
|
|
3041
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3081
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3042
3082
|
target = os.path.join(folder, name)
|
|
3043
3083
|
dump_ec_session(target, fig=fig, ax=ax, cycle_lines=cycle_lines, skip_confirm=True)
|
|
3044
3084
|
fig._last_session_save_path = target
|
|
3045
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3085
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3046
3086
|
else:
|
|
3047
3087
|
print("Invalid number.")
|
|
3048
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3088
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3049
3089
|
if choice.lower() != 'o':
|
|
3050
3090
|
name = choice
|
|
3051
3091
|
root, ext = os.path.splitext(name)
|
|
@@ -3055,12 +3095,12 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3055
3095
|
if os.path.exists(target):
|
|
3056
3096
|
yn = _safe_input(f"'{os.path.basename(target)}' exists. Overwrite? (y/n): ").strip().lower()
|
|
3057
3097
|
if yn != 'y':
|
|
3058
|
-
_print_menu(len(all_cycles), is_dqdv); continue
|
|
3098
|
+
_print_menu(len(all_cycles), is_dqdv, fig); continue
|
|
3059
3099
|
dump_ec_session(target, fig=fig, ax=ax, cycle_lines=cycle_lines, skip_confirm=True)
|
|
3060
3100
|
fig._last_session_save_path = target
|
|
3061
3101
|
except Exception as e:
|
|
3062
3102
|
print(f"Save failed: {e}")
|
|
3063
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3103
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3064
3104
|
continue
|
|
3065
3105
|
elif key == 'c':
|
|
3066
3106
|
# Show current palette if one is applied (this is informational only)
|
|
@@ -3096,7 +3136,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3096
3136
|
continue
|
|
3097
3137
|
if line.lower() == 'u':
|
|
3098
3138
|
manage_user_colors(fig)
|
|
3099
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3139
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3100
3140
|
continue
|
|
3101
3141
|
tokens = line.replace(',', ' ').split()
|
|
3102
3142
|
mode, cycles, mapping, palette, use_all = _parse_cycle_tokens(tokens, fig)
|
|
@@ -3225,13 +3265,13 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3225
3265
|
if ignored:
|
|
3226
3266
|
print("Ignored cycles:", ", ".join(str(c) for c in ignored))
|
|
3227
3267
|
# Show the menu again after completing the command
|
|
3228
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3268
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3229
3269
|
continue
|
|
3230
3270
|
elif key == 'a':
|
|
3231
3271
|
# X-axis submenu: number-of-ions vs capacity (not available in dQdV mode)
|
|
3232
3272
|
if is_dqdv:
|
|
3233
3273
|
print("Capacity/ion conversion is not available in dQ/dV mode.")
|
|
3234
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3274
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3235
3275
|
continue
|
|
3236
3276
|
# X-axis submenu: number-of-ions vs capacity
|
|
3237
3277
|
while True:
|
|
@@ -3372,7 +3412,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3372
3412
|
fig.canvas.draw()
|
|
3373
3413
|
except Exception:
|
|
3374
3414
|
fig.canvas.draw_idle()
|
|
3375
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3415
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3376
3416
|
continue
|
|
3377
3417
|
elif key == 'f':
|
|
3378
3418
|
# Font submenu with numbered options
|
|
@@ -3443,7 +3483,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3443
3483
|
print("Size must be positive.")
|
|
3444
3484
|
except Exception:
|
|
3445
3485
|
print("Invalid size.")
|
|
3446
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3486
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3447
3487
|
continue
|
|
3448
3488
|
elif key == 'x':
|
|
3449
3489
|
# X-axis: set limits only
|
|
@@ -3527,7 +3567,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3527
3567
|
fig.canvas.draw()
|
|
3528
3568
|
except Exception:
|
|
3529
3569
|
print("Invalid limits, ignored.")
|
|
3530
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3570
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3531
3571
|
continue
|
|
3532
3572
|
elif key == 'y':
|
|
3533
3573
|
# Y-axis: set limits only
|
|
@@ -3611,7 +3651,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3611
3651
|
fig.canvas.draw()
|
|
3612
3652
|
except Exception:
|
|
3613
3653
|
print("Invalid limits, ignored.")
|
|
3614
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3654
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3615
3655
|
continue
|
|
3616
3656
|
elif key == 'g':
|
|
3617
3657
|
# Geometry submenu: plot frame vs canvas (scales moved to separate keys)
|
|
@@ -3640,13 +3680,13 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3640
3680
|
fig.canvas.draw()
|
|
3641
3681
|
except Exception:
|
|
3642
3682
|
fig.canvas.draw_idle()
|
|
3643
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3683
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3644
3684
|
continue
|
|
3645
3685
|
elif key == 'sm':
|
|
3646
3686
|
# dQ/dV smoothing utilities (only available in dQdV mode)
|
|
3647
3687
|
if not is_dqdv:
|
|
3648
3688
|
print("Smoothing is only available in dQ/dV mode.")
|
|
3649
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
3689
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
3650
3690
|
continue
|
|
3651
3691
|
while True:
|
|
3652
3692
|
print("\n\033[1mdQ/dV Data Filtering (Neware method)\033[0m")
|
|
@@ -4006,11 +4046,11 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
4006
4046
|
print("Invalid number.")
|
|
4007
4047
|
continue
|
|
4008
4048
|
print("Unknown command. Use a/o/r/q.")
|
|
4009
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
4049
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
4010
4050
|
continue
|
|
4011
4051
|
else:
|
|
4012
4052
|
print("Unknown command.")
|
|
4013
|
-
_print_menu(len(all_cycles), is_dqdv)
|
|
4053
|
+
_print_menu(len(all_cycles), is_dqdv, fig)
|
|
4014
4054
|
|
|
4015
4055
|
|
|
4016
4056
|
def _get_geometry_snapshot(fig, ax) -> Dict:
|