batplot 1.7.28__py3-none-any.whl → 1.8.0__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.
Potentially problematic release.
This version of batplot might be problematic. Click here for more details.
- batplot/__init__.py +1 -1
- batplot/cpc_interactive.py +3 -0
- batplot/electrochem_interactive.py +90 -55
- batplot/interactive.py +92 -61
- batplot/modes.py +12 -12
- batplot/operando_ec_interactive.py +144 -74
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/METADATA +1 -1
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/RECORD +12 -12
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/WHEEL +0 -0
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/entry_points.txt +0 -0
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.7.28.dist-info → batplot-1.8.0.dist-info}/top_level.txt +0 -0
batplot/interactive.py
CHANGED
|
@@ -58,6 +58,37 @@ from .color_utils import (
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
class _FilterIMKWarning:
|
|
62
|
+
"""Filter that suppresses macOS IMKCFRunLoopWakeUpReliable warnings while preserving other errors."""
|
|
63
|
+
def __init__(self, original_stderr):
|
|
64
|
+
self.original_stderr = original_stderr
|
|
65
|
+
|
|
66
|
+
def write(self, message):
|
|
67
|
+
# Filter out the harmless macOS IMK warning
|
|
68
|
+
if 'IMKCFRunLoopWakeUpReliable' not in message:
|
|
69
|
+
self.original_stderr.write(message)
|
|
70
|
+
|
|
71
|
+
def flush(self):
|
|
72
|
+
self.original_stderr.flush()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _safe_input(prompt: str = "") -> str:
|
|
76
|
+
"""Wrapper around input() that suppresses macOS IMKCFRunLoopWakeUpReliable warnings.
|
|
77
|
+
|
|
78
|
+
This is a harmless macOS system message that appears when using input() in terminals.
|
|
79
|
+
"""
|
|
80
|
+
# Filter stderr to hide macOS IMK warnings while preserving other errors
|
|
81
|
+
original_stderr = sys.stderr
|
|
82
|
+
sys.stderr = _FilterIMKWarning(original_stderr)
|
|
83
|
+
try:
|
|
84
|
+
result = input(prompt)
|
|
85
|
+
return result
|
|
86
|
+
except (KeyboardInterrupt, EOFError):
|
|
87
|
+
raise
|
|
88
|
+
finally:
|
|
89
|
+
sys.stderr = original_stderr
|
|
90
|
+
|
|
91
|
+
|
|
61
92
|
def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
62
93
|
label_text_objects, delta, x_label, args,
|
|
63
94
|
x_full_list, raw_y_full_list, offsets_list,
|
|
@@ -363,7 +394,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
363
394
|
current_y_px = _px_value('_top_xlabel_manual_offset_y_pts')
|
|
364
395
|
current_x_px = _px_value('_top_xlabel_manual_offset_x_pts')
|
|
365
396
|
print(f"Top title offset: Y={current_y_px:+.2f} px (positive=up), X={current_x_px:+.2f} px (positive=right)")
|
|
366
|
-
sub =
|
|
397
|
+
sub = _safe_input(colorize_prompt("top (w=up, s=down, a=left, d=right, 0=reset, q=back): ")).strip().lower()
|
|
367
398
|
if not sub:
|
|
368
399
|
continue
|
|
369
400
|
if sub == 'q':
|
|
@@ -401,7 +432,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
401
432
|
current_x_px = _px_value('_right_ylabel_manual_offset_x_pts')
|
|
402
433
|
current_y_px = _px_value('_right_ylabel_manual_offset_y_pts')
|
|
403
434
|
print(f"Right title offset: X={current_x_px:+.2f} px (positive=right), Y={current_y_px:+.2f} px (positive=up)")
|
|
404
|
-
sub =
|
|
435
|
+
sub = _safe_input(colorize_prompt("right (d=right, a=left, w=up, s=down, 0=reset, q=back): ")).strip().lower()
|
|
405
436
|
if not sub:
|
|
406
437
|
continue
|
|
407
438
|
if sub == 'q':
|
|
@@ -438,7 +469,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
438
469
|
while True:
|
|
439
470
|
current_y_px = _px_value('_bottom_xlabel_manual_offset_y_pts')
|
|
440
471
|
print(f"Bottom title offset: Y={current_y_px:+.2f} px (positive=down)")
|
|
441
|
-
sub =
|
|
472
|
+
sub = _safe_input(colorize_prompt("bottom (s=down, w=up, 0=reset, q=back): ")).strip().lower()
|
|
442
473
|
if not sub:
|
|
443
474
|
continue
|
|
444
475
|
if sub == 'q':
|
|
@@ -468,7 +499,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
468
499
|
while True:
|
|
469
500
|
current_x_px = _px_value('_left_ylabel_manual_offset_x_pts')
|
|
470
501
|
print(f"Left title offset: X={current_x_px:+.2f} px (positive=left)")
|
|
471
|
-
sub =
|
|
502
|
+
sub = _safe_input(colorize_prompt("left (a=left, d=right, 0=reset, q=back): ")).strip().lower()
|
|
472
503
|
if not sub:
|
|
473
504
|
continue
|
|
474
505
|
if sub == 'q':
|
|
@@ -499,7 +530,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
499
530
|
print(" " + colorize_menu('d : adjust right title (d=right, a=left, w=up, s=down)'))
|
|
500
531
|
print(" " + colorize_menu('r : reset all offsets'))
|
|
501
532
|
print(" " + colorize_menu('q : back to toggle menu'))
|
|
502
|
-
choice =
|
|
533
|
+
choice = _safe_input(colorize_prompt("p> ")).strip().lower()
|
|
503
534
|
if not choice:
|
|
504
535
|
continue
|
|
505
536
|
if choice == 'q':
|
|
@@ -618,7 +649,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
618
649
|
|
|
619
650
|
while True:
|
|
620
651
|
render()
|
|
621
|
-
cmd =
|
|
652
|
+
cmd = _safe_input("> ").strip().lower()
|
|
622
653
|
if cmd == 'q':
|
|
623
654
|
print("Exited game. Returning to interactive menu.\n")
|
|
624
655
|
break
|
|
@@ -947,7 +978,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
947
978
|
# Only ask for wavelength if it's diffraction data, not using Q, and no file wavelength info
|
|
948
979
|
if is_diffraction and not use_Q and not file_wavelength_info:
|
|
949
980
|
try:
|
|
950
|
-
wl_in =
|
|
981
|
+
wl_in = _safe_input("Enter wavelength in Å for Q,d display (blank=skip, q=cancel): ").strip()
|
|
951
982
|
if wl_in.lower() == 'q':
|
|
952
983
|
print("Canceled.")
|
|
953
984
|
return
|
|
@@ -1445,7 +1476,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1445
1476
|
while True:
|
|
1446
1477
|
try:
|
|
1447
1478
|
print_main_menu()
|
|
1448
|
-
key =
|
|
1479
|
+
key = _safe_input("Press a key: ").strip().lower()
|
|
1449
1480
|
except (KeyboardInterrupt, EOFError):
|
|
1450
1481
|
print("\n\nExiting interactive menu...")
|
|
1451
1482
|
break
|
|
@@ -1460,7 +1491,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1460
1491
|
|
|
1461
1492
|
if key == 'q':
|
|
1462
1493
|
try:
|
|
1463
|
-
confirm =
|
|
1494
|
+
confirm = _safe_input(colorize_prompt("Quit interactive? Remember to save (e=export, s=save). Quit now? (y/n): ")).strip().lower()
|
|
1464
1495
|
except (KeyboardInterrupt, EOFError):
|
|
1465
1496
|
print("\nExiting interactive menu...")
|
|
1466
1497
|
break
|
|
@@ -1514,7 +1545,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1514
1545
|
current_pos = _current_label_position()
|
|
1515
1546
|
print(f" {colorize_menu(f's: legend position (current: {current_pos})')}")
|
|
1516
1547
|
print(f" {colorize_menu('q: back to main menu')}")
|
|
1517
|
-
sub_key =
|
|
1548
|
+
sub_key = _safe_input("Choose: ").strip().lower()
|
|
1518
1549
|
|
|
1519
1550
|
if sub_key == 'q':
|
|
1520
1551
|
break
|
|
@@ -1536,7 +1567,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1536
1567
|
print(" 2: top-left")
|
|
1537
1568
|
print(" 3: bottom-right")
|
|
1538
1569
|
print(" 4: bottom-left")
|
|
1539
|
-
choice =
|
|
1570
|
+
choice = _safe_input("Position (1-4, q=cancel): ").strip().lower()
|
|
1540
1571
|
options = {
|
|
1541
1572
|
'1': (False, False),
|
|
1542
1573
|
'2': (False, True),
|
|
@@ -1638,7 +1669,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1638
1669
|
prompt = "Enter new filename (no ext needed), number to overwrite, or o to overwrite last (q=cancel): "
|
|
1639
1670
|
else:
|
|
1640
1671
|
prompt = "Enter new filename (no ext needed) or number to overwrite (q=cancel): "
|
|
1641
|
-
choice =
|
|
1672
|
+
choice = _safe_input(prompt).strip()
|
|
1642
1673
|
if not choice or choice.lower() == 'q':
|
|
1643
1674
|
print("Canceled.")
|
|
1644
1675
|
continue
|
|
@@ -1650,7 +1681,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1650
1681
|
if not os.path.exists(last_session_path):
|
|
1651
1682
|
print(f"Previous save file not found: {last_session_path}")
|
|
1652
1683
|
continue
|
|
1653
|
-
yn =
|
|
1684
|
+
yn = _safe_input(f"Overwrite '{os.path.basename(last_session_path)}'? (y/n): ").strip().lower()
|
|
1654
1685
|
if yn != 'y':
|
|
1655
1686
|
continue
|
|
1656
1687
|
_bp_dump_session(
|
|
@@ -1680,7 +1711,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1680
1711
|
idx = int(choice)
|
|
1681
1712
|
if 1 <= idx <= len(files):
|
|
1682
1713
|
name = files[idx-1]
|
|
1683
|
-
yn =
|
|
1714
|
+
yn = _safe_input(f"Overwrite '{name}'? (y/n): ").strip().lower()
|
|
1684
1715
|
if yn != 'y':
|
|
1685
1716
|
print("Canceled.")
|
|
1686
1717
|
continue
|
|
@@ -1720,7 +1751,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1720
1751
|
target_path = name if os.path.isabs(name) else os.path.join(folder, name)
|
|
1721
1752
|
skip_confirm = False # Let dump_session ask
|
|
1722
1753
|
if os.path.exists(target_path):
|
|
1723
|
-
yn =
|
|
1754
|
+
yn = _safe_input(f"'{os.path.basename(target_path)}' exists. Overwrite? (y/n): ").strip().lower()
|
|
1724
1755
|
if yn != 'y':
|
|
1725
1756
|
print("Canceled.")
|
|
1726
1757
|
continue
|
|
@@ -1772,7 +1803,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1772
1803
|
print(f" {colorize_menu('t : change CIF tick set color (e.g., 1:red 2:#888888)')}")
|
|
1773
1804
|
print(f" {colorize_menu('u : manage saved colors (use in m/p via number or u#)')}")
|
|
1774
1805
|
print(f" {colorize_menu('q : return to main menu')}")
|
|
1775
|
-
sub =
|
|
1806
|
+
sub = _safe_input(colorize_prompt("Choose (m/p/s/t/u/q): ")).strip().lower()
|
|
1776
1807
|
if sub == 'q':
|
|
1777
1808
|
break
|
|
1778
1809
|
if sub == '':
|
|
@@ -1790,7 +1821,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1790
1821
|
print("\nSaved colors (refer as number or u#):")
|
|
1791
1822
|
for idx, color in enumerate(user_colors, 1):
|
|
1792
1823
|
print(f" {idx}: {color_block(color)} {color}")
|
|
1793
|
-
color_input =
|
|
1824
|
+
color_input = _safe_input("Enter curve+color pairs (e.g., 1 red 2:u3) or q: ").strip()
|
|
1794
1825
|
if not color_input or color_input.lower() == 'q':
|
|
1795
1826
|
print("Canceled.")
|
|
1796
1827
|
else:
|
|
@@ -1844,7 +1875,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1844
1875
|
for idx, color in enumerate(user_colors, 1):
|
|
1845
1876
|
print(f" {idx}: {color_block(color)} {color}")
|
|
1846
1877
|
print("Type 'u' to edit saved colors.")
|
|
1847
|
-
line =
|
|
1878
|
+
line = _safe_input("Enter mappings (e.g., w red a u3) or q: ").strip()
|
|
1848
1879
|
if line.lower() == 'u':
|
|
1849
1880
|
manage_user_colors(fig)
|
|
1850
1881
|
continue
|
|
@@ -1900,7 +1931,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1900
1931
|
print("Current CIF tick sets:")
|
|
1901
1932
|
for i,(lab, fname, *_rest) in enumerate(cts):
|
|
1902
1933
|
print(f" {i+1}: {lab} ({os.path.basename(fname)})")
|
|
1903
|
-
line =
|
|
1934
|
+
line = _safe_input("Enter mappings (e.g., 1:red 2:#555555) or q: ").strip()
|
|
1904
1935
|
if not line or line.lower()=='q':
|
|
1905
1936
|
print("Canceled.")
|
|
1906
1937
|
else:
|
|
@@ -1966,7 +1997,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1966
1997
|
if bar:
|
|
1967
1998
|
print(f" {bar}")
|
|
1968
1999
|
print(colorize_inline_commands("Example: 1-4 viridis or: all magma_r or: 1-3,5 plasma, _r for reverse"))
|
|
1969
|
-
line =
|
|
2000
|
+
line = _safe_input("Enter range(s) and palette (number or name, e.g., '1-3 2' or 'all 1_r') or q: ").strip()
|
|
1970
2001
|
if not line or line.lower() == 'q':
|
|
1971
2002
|
print("Canceled.")
|
|
1972
2003
|
else:
|
|
@@ -2171,7 +2202,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2171
2202
|
if has_cif:
|
|
2172
2203
|
rename_opts += ", t=cif tick label"
|
|
2173
2204
|
rename_opts += ", x=x-axis, y=y-axis, q=return"
|
|
2174
|
-
mode =
|
|
2205
|
+
mode = _safe_input(f"Rename ({rename_opts}): ").strip().lower()
|
|
2175
2206
|
if mode == 'q':
|
|
2176
2207
|
break
|
|
2177
2208
|
if mode == '':
|
|
@@ -2180,7 +2211,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2180
2211
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
2181
2212
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
2182
2213
|
print(" Bullet: $\\bullet$ → • | Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2183
|
-
idx_in =
|
|
2214
|
+
idx_in = _safe_input("Curve number to rename (q=cancel): ").strip()
|
|
2184
2215
|
if not idx_in or idx_in.lower() == 'q':
|
|
2185
2216
|
print("Canceled.")
|
|
2186
2217
|
continue
|
|
@@ -2192,7 +2223,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2192
2223
|
if not (0 <= idx < len(labels)):
|
|
2193
2224
|
print("Invalid index.")
|
|
2194
2225
|
continue
|
|
2195
|
-
new_label =
|
|
2226
|
+
new_label = _safe_input("New curve label (q=cancel): ")
|
|
2196
2227
|
if not new_label or new_label.lower() == 'q':
|
|
2197
2228
|
print("Canceled.")
|
|
2198
2229
|
continue
|
|
@@ -2207,7 +2238,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2207
2238
|
continue
|
|
2208
2239
|
for i,(lab, fname, *_rest) in enumerate(cts):
|
|
2209
2240
|
print(f" {i+1}: {lab} ({os.path.basename(fname)})")
|
|
2210
|
-
s =
|
|
2241
|
+
s = _safe_input("CIF tick number to rename (q=cancel): ").strip()
|
|
2211
2242
|
if not s or s.lower()=='q':
|
|
2212
2243
|
print("Canceled."); continue
|
|
2213
2244
|
try:
|
|
@@ -2219,7 +2250,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2219
2250
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
2220
2251
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
2221
2252
|
print(" Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2222
|
-
new_name =
|
|
2253
|
+
new_name = _safe_input("New CIF tick label (q=cancel): ")
|
|
2223
2254
|
if not new_name or new_name.lower()=='q':
|
|
2224
2255
|
print("Canceled."); continue
|
|
2225
2256
|
lab,fname,peaksQ,wl,qmax_sim,color = cts[idx]
|
|
@@ -2247,7 +2278,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2247
2278
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
2248
2279
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
2249
2280
|
print(" Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2250
|
-
new_axis =
|
|
2281
|
+
new_axis = _safe_input("New axis label: ")
|
|
2251
2282
|
if not new_axis or new_axis.lower() == 'q':
|
|
2252
2283
|
print("Canceled.")
|
|
2253
2284
|
continue
|
|
@@ -2296,7 +2327,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2296
2327
|
print("Current curve order:")
|
|
2297
2328
|
for idx, label in enumerate(labels):
|
|
2298
2329
|
print(f"{idx+1}: {label}")
|
|
2299
|
-
new_order_str =
|
|
2330
|
+
new_order_str = _safe_input("Enter new order (space-separated indices, q=cancel): ").strip()
|
|
2300
2331
|
if not new_order_str or new_order_str.lower() == 'q':
|
|
2301
2332
|
print("Canceled.")
|
|
2302
2333
|
continue
|
|
@@ -2386,7 +2417,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2386
2417
|
try:
|
|
2387
2418
|
current_xlim = ax.get_xlim()
|
|
2388
2419
|
print(f"Current X range: {current_xlim[0]:.6g} to {current_xlim[1]:.6g}")
|
|
2389
|
-
rng =
|
|
2420
|
+
rng = _safe_input("Enter new X range (min max), w=upper only, s=lower only, 'full', or 'a'=auto (restore original) (q=back): ").strip()
|
|
2390
2421
|
if not rng or rng.lower() == 'q':
|
|
2391
2422
|
break
|
|
2392
2423
|
if rng.lower() == 'w':
|
|
@@ -2394,7 +2425,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2394
2425
|
while True:
|
|
2395
2426
|
current_xlim = ax.get_xlim()
|
|
2396
2427
|
print(f"Current X range: {current_xlim[0]:.6g} to {current_xlim[1]:.6g}")
|
|
2397
|
-
val =
|
|
2428
|
+
val = _safe_input(f"Enter new upper X limit (current lower: {current_xlim[0]:.6g}, q=back): ").strip()
|
|
2398
2429
|
if not val or val.lower() == 'q':
|
|
2399
2430
|
break
|
|
2400
2431
|
try:
|
|
@@ -2425,7 +2456,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2425
2456
|
while True:
|
|
2426
2457
|
current_xlim = ax.get_xlim()
|
|
2427
2458
|
print(f"Current X range: {current_xlim[0]:.6g} to {current_xlim[1]:.6g}")
|
|
2428
|
-
val =
|
|
2459
|
+
val = _safe_input(f"Enter new lower X limit (current upper: {current_xlim[1]:.6g}, q=back): ").strip()
|
|
2429
2460
|
if not val or val.lower() == 'q':
|
|
2430
2461
|
break
|
|
2431
2462
|
try:
|
|
@@ -2560,7 +2591,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2560
2591
|
try:
|
|
2561
2592
|
current_ylim = ax.get_ylim()
|
|
2562
2593
|
print(f"Current Y range: {current_ylim[0]:.6g} to {current_ylim[1]:.6g}")
|
|
2563
|
-
rng =
|
|
2594
|
+
rng = _safe_input("Enter new Y range (min max), w=upper only, s=lower only, 'auto', 'a'=auto (restore original), or 'full' (q=back): ").strip().lower()
|
|
2564
2595
|
if not rng or rng == 'q':
|
|
2565
2596
|
break
|
|
2566
2597
|
if rng == 'w':
|
|
@@ -2568,7 +2599,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2568
2599
|
while True:
|
|
2569
2600
|
current_ylim = ax.get_ylim()
|
|
2570
2601
|
print(f"Current Y range: {current_ylim[0]:.6g} to {current_ylim[1]:.6g}")
|
|
2571
|
-
val =
|
|
2602
|
+
val = _safe_input(f"Enter new upper Y limit (current lower: {current_ylim[0]:.6g}, q=back): ").strip()
|
|
2572
2603
|
if not val or val.lower() == 'q':
|
|
2573
2604
|
break
|
|
2574
2605
|
try:
|
|
@@ -2590,7 +2621,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2590
2621
|
while True:
|
|
2591
2622
|
current_ylim = ax.get_ylim()
|
|
2592
2623
|
print(f"Current Y range: {current_ylim[0]:.6g} to {current_ylim[1]:.6g}")
|
|
2593
|
-
val =
|
|
2624
|
+
val = _safe_input(f"Enter new lower Y limit (current upper: {current_ylim[1]:.6g}, q=back): ").strip()
|
|
2594
2625
|
if not val or val.lower() == 'q':
|
|
2595
2626
|
break
|
|
2596
2627
|
try:
|
|
@@ -2675,7 +2706,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2675
2706
|
print(f" {colorize_menu('q: back to main menu')}")
|
|
2676
2707
|
|
|
2677
2708
|
while True:
|
|
2678
|
-
offset_cmd =
|
|
2709
|
+
offset_cmd = _safe_input("Offset> ").strip().lower()
|
|
2679
2710
|
|
|
2680
2711
|
if offset_cmd == 'q' or offset_cmd == '':
|
|
2681
2712
|
break
|
|
@@ -2725,7 +2756,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2725
2756
|
if spacing_diffs:
|
|
2726
2757
|
current_spacing = sum(spacing_diffs) / len(spacing_diffs)
|
|
2727
2758
|
|
|
2728
|
-
spacing_input =
|
|
2759
|
+
spacing_input = _safe_input("Enter spacing value between curves (current avg: {:.4g}): ".format(current_spacing)).strip()
|
|
2729
2760
|
if not spacing_input:
|
|
2730
2761
|
print("Canceled.")
|
|
2731
2762
|
continue
|
|
@@ -2784,7 +2815,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2784
2815
|
if len(labels) <= 1:
|
|
2785
2816
|
print("Warning: Only one curve loaded; applying an offset is not recommended.")
|
|
2786
2817
|
try:
|
|
2787
|
-
new_delta_str =
|
|
2818
|
+
new_delta_str = _safe_input(f"Enter new offset spacing (current={delta}): ").strip()
|
|
2788
2819
|
if not new_delta_str:
|
|
2789
2820
|
print("Canceled.")
|
|
2790
2821
|
continue
|
|
@@ -2847,7 +2878,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2847
2878
|
|
|
2848
2879
|
current_offset = offsets_list[idx] if idx < len(offsets_list) else 0.0
|
|
2849
2880
|
|
|
2850
|
-
individual_offset_input =
|
|
2881
|
+
individual_offset_input = _safe_input("Enter offset for curve {} (current: {:.4g}): ".format(
|
|
2851
2882
|
curve_num, current_offset)).strip()
|
|
2852
2883
|
if not individual_offset_input:
|
|
2853
2884
|
print("Canceled.")
|
|
@@ -2887,7 +2918,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2887
2918
|
print("No curves to modify.")
|
|
2888
2919
|
return []
|
|
2889
2920
|
print(f"Total curves available: {total}")
|
|
2890
|
-
raw =
|
|
2921
|
+
raw = _safe_input(prompt_text + " ").strip().lower()
|
|
2891
2922
|
if not raw or raw in ('all', '*'):
|
|
2892
2923
|
return list(range(total))
|
|
2893
2924
|
import re as _re
|
|
@@ -2906,7 +2937,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2906
2937
|
return selected
|
|
2907
2938
|
|
|
2908
2939
|
def _prompt_float(prompt_text):
|
|
2909
|
-
raw =
|
|
2940
|
+
raw = _safe_input(prompt_text).strip()
|
|
2910
2941
|
if not raw:
|
|
2911
2942
|
return None
|
|
2912
2943
|
if raw.lower() == 'q':
|
|
@@ -2919,10 +2950,10 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2919
2950
|
|
|
2920
2951
|
def _prompt_dash_pattern(kind='dash'):
|
|
2921
2952
|
if kind == 'dashdot':
|
|
2922
|
-
raw =
|
|
2953
|
+
raw = _safe_input("Dash-dot pattern 'dash gap dot gap' (blank=6 3 1 3, q=cancel): ").strip().lower()
|
|
2923
2954
|
default = (6.0, 3.0, 1.0, 3.0)
|
|
2924
2955
|
else:
|
|
2925
|
-
raw =
|
|
2956
|
+
raw = _safe_input("Dash pattern 'length gap' (blank=6 3, q=cancel): ").strip().lower()
|
|
2926
2957
|
default = (6.0, 3.0)
|
|
2927
2958
|
if not raw:
|
|
2928
2959
|
return default
|
|
@@ -2962,13 +2993,13 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2962
2993
|
print(f" {colorize_menu('da : dashed line for selected curves')}")
|
|
2963
2994
|
print(f" {colorize_menu('dd : dashed line + dots for selected curves')}")
|
|
2964
2995
|
print(f" {colorize_menu('q : return')}")
|
|
2965
|
-
sub =
|
|
2996
|
+
sub = _safe_input(colorize_prompt("Choose (c/f/g/l/ld/d/da/dd/q): ")).strip().lower()
|
|
2966
2997
|
if sub == 'q':
|
|
2967
2998
|
break
|
|
2968
2999
|
if sub == '':
|
|
2969
3000
|
continue
|
|
2970
3001
|
if sub == 'c':
|
|
2971
|
-
spec =
|
|
3002
|
+
spec = _safe_input("Curve widths (single value OR mappings like '1:1.2 3:2', q=cancel): ").strip()
|
|
2972
3003
|
if not spec or spec.lower() == 'q':
|
|
2973
3004
|
print("Canceled.")
|
|
2974
3005
|
else:
|
|
@@ -2998,7 +3029,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2998
3029
|
print("Invalid width value.")
|
|
2999
3030
|
fig.canvas.draw()
|
|
3000
3031
|
elif sub == 'f':
|
|
3001
|
-
fw_in =
|
|
3032
|
+
fw_in = _safe_input("Enter frame/tick width (e.g., 1.5) or 'm M' (major minor) or q: ").strip()
|
|
3002
3033
|
if not fw_in or fw_in.lower() == 'q':
|
|
3003
3034
|
print("Canceled.")
|
|
3004
3035
|
else:
|
|
@@ -3133,7 +3164,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3133
3164
|
cur_family = plt.rcParams.get('font.sans-serif', [''])[0]
|
|
3134
3165
|
cur_size = plt.rcParams.get('font.size', None)
|
|
3135
3166
|
while True:
|
|
3136
|
-
subkey =
|
|
3167
|
+
subkey = _safe_input(colorize_prompt(f"Font submenu (current: family='{cur_family}', size={cur_size}) - s=size, f=family, q=return: ")).strip().lower()
|
|
3137
3168
|
if subkey == 'q':
|
|
3138
3169
|
break
|
|
3139
3170
|
if subkey == '':
|
|
@@ -3141,7 +3172,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3141
3172
|
if subkey == 's':
|
|
3142
3173
|
try:
|
|
3143
3174
|
cur_size = plt.rcParams.get('font.size', None)
|
|
3144
|
-
fs =
|
|
3175
|
+
fs = _safe_input(f"Enter new font size (current: {cur_size}, q=cancel): ").strip()
|
|
3145
3176
|
if not fs or fs.lower() == 'q':
|
|
3146
3177
|
print("Canceled.")
|
|
3147
3178
|
else:
|
|
@@ -3163,7 +3194,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3163
3194
|
print(" 3) Times New Roman")
|
|
3164
3195
|
print(" 4) STIXGeneral")
|
|
3165
3196
|
print(" 5) DejaVu Sans")
|
|
3166
|
-
ft_raw =
|
|
3197
|
+
ft_raw = _safe_input(f"Enter font number or family name (current: '{cur_family}', q=cancel): ").strip()
|
|
3167
3198
|
if not ft_raw or ft_raw.lower() == 'q':
|
|
3168
3199
|
print("Canceled.")
|
|
3169
3200
|
else:
|
|
@@ -3189,7 +3220,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3189
3220
|
elif key == 'g':
|
|
3190
3221
|
try:
|
|
3191
3222
|
while True:
|
|
3192
|
-
choice =
|
|
3223
|
+
choice = _safe_input(colorize_prompt("Resize submenu: (p=plot frame, c=canvas, q=cancel): ")).strip().lower()
|
|
3193
3224
|
if not choice:
|
|
3194
3225
|
continue
|
|
3195
3226
|
if choice == 'q':
|
|
@@ -3214,7 +3245,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3214
3245
|
current_pos = "bottom-right" if getattr(fig, '_stack_label_at_bottom', False) else "top-right"
|
|
3215
3246
|
print(f" s: legend position (current: {current_pos})")
|
|
3216
3247
|
print(" q: back to main menu")
|
|
3217
|
-
sub_key =
|
|
3248
|
+
sub_key = _safe_input("Choose: ").strip().lower()
|
|
3218
3249
|
|
|
3219
3250
|
if sub_key == 'q':
|
|
3220
3251
|
break
|
|
@@ -3272,7 +3303,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3272
3303
|
print(colorize_inline_commands(" Combine letter+number to toggle, e.g. 's2 w5 a4' (case-insensitive)"))
|
|
3273
3304
|
print(colorize_inline_commands(" i = invert tick direction, l = change tick length, list = show state, q = return"))
|
|
3274
3305
|
print(colorize_inline_commands(" p = adjust title offsets (w=top, s=bottom, a=left, d=right)"))
|
|
3275
|
-
cmd =
|
|
3306
|
+
cmd = _safe_input(colorize_prompt("Enter code(s): ")).strip().lower()
|
|
3276
3307
|
if not cmd:
|
|
3277
3308
|
continue
|
|
3278
3309
|
if cmd == 'q':
|
|
@@ -3299,7 +3330,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3299
3330
|
# Get current major tick length from axes
|
|
3300
3331
|
current_major = ax.xaxis.get_major_ticks()[0].tick1line.get_markersize() if ax.xaxis.get_major_ticks() else 4.0
|
|
3301
3332
|
print(f"Current major tick length: {current_major}")
|
|
3302
|
-
new_length_str =
|
|
3333
|
+
new_length_str = _safe_input("Enter new major tick length (e.g., 6.0): ").strip()
|
|
3303
3334
|
if not new_length_str:
|
|
3304
3335
|
continue
|
|
3305
3336
|
new_major = float(new_length_str)
|
|
@@ -3582,9 +3613,9 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3582
3613
|
print(f" {_i}: {fname}")
|
|
3583
3614
|
last_style_path = getattr(fig, '_last_style_export_path', None)
|
|
3584
3615
|
if last_style_path:
|
|
3585
|
-
sub =
|
|
3616
|
+
sub = _safe_input(colorize_prompt("Style submenu: (e=export, o=overwrite last, q=return, r=refresh): ")).strip().lower()
|
|
3586
3617
|
else:
|
|
3587
|
-
sub =
|
|
3618
|
+
sub = _safe_input(colorize_prompt("Style submenu: (e=export, q=return, r=refresh): ")).strip().lower()
|
|
3588
3619
|
if sub == 'q':
|
|
3589
3620
|
break
|
|
3590
3621
|
if sub == 'r' or sub == '':
|
|
@@ -3597,7 +3628,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3597
3628
|
if not os.path.exists(last_style_path):
|
|
3598
3629
|
print(f"Previous export file not found: {last_style_path}")
|
|
3599
3630
|
continue
|
|
3600
|
-
yn =
|
|
3631
|
+
yn = _safe_input(f"Overwrite '{os.path.basename(last_style_path)}'? (y/n): ").strip().lower()
|
|
3601
3632
|
if yn != 'y':
|
|
3602
3633
|
continue
|
|
3603
3634
|
# Call export_style_config with overwrite_path to skip dialog
|
|
@@ -3655,9 +3686,9 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3655
3686
|
|
|
3656
3687
|
last_figure_path = getattr(fig, '_last_figure_export_path', None)
|
|
3657
3688
|
if last_figure_path:
|
|
3658
|
-
filename =
|
|
3689
|
+
filename = _safe_input("Enter filename (default SVG if no extension), number to overwrite, or o to overwrite last (q=cancel): ").strip()
|
|
3659
3690
|
else:
|
|
3660
|
-
filename =
|
|
3691
|
+
filename = _safe_input("Enter filename (default SVG if no extension) or number to overwrite (q=cancel): ").strip()
|
|
3661
3692
|
if not filename or filename.lower() == 'q':
|
|
3662
3693
|
print("Canceled.")
|
|
3663
3694
|
continue
|
|
@@ -3671,7 +3702,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3671
3702
|
if not os.path.exists(last_figure_path):
|
|
3672
3703
|
print(f"Previous export file not found: {last_figure_path}")
|
|
3673
3704
|
continue
|
|
3674
|
-
yn =
|
|
3705
|
+
yn = _safe_input(f"Overwrite '{os.path.basename(last_figure_path)}'? (y/n): ").strip().lower()
|
|
3675
3706
|
if yn != 'y':
|
|
3676
3707
|
print("Canceled.")
|
|
3677
3708
|
continue
|
|
@@ -3683,7 +3714,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3683
3714
|
idx = int(filename)
|
|
3684
3715
|
if 1 <= idx <= len(files):
|
|
3685
3716
|
name = files[idx-1]
|
|
3686
|
-
yn =
|
|
3717
|
+
yn = _safe_input(f"Overwrite '{name}'? (y/n): ").strip().lower()
|
|
3687
3718
|
if yn != 'y':
|
|
3688
3719
|
print("Canceled.")
|
|
3689
3720
|
continue
|
|
@@ -3759,7 +3790,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3759
3790
|
elif key == 'v':
|
|
3760
3791
|
while True:
|
|
3761
3792
|
try:
|
|
3762
|
-
rng_in =
|
|
3793
|
+
rng_in = _safe_input("Peak X range (min max, 'current' for axes limits, q=back): ").strip().lower()
|
|
3763
3794
|
if not rng_in or rng_in == 'q':
|
|
3764
3795
|
break
|
|
3765
3796
|
if rng_in == 'current':
|
|
@@ -3773,12 +3804,12 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3773
3804
|
if x_min > x_max:
|
|
3774
3805
|
x_min, x_max = x_max, x_min
|
|
3775
3806
|
|
|
3776
|
-
frac_in =
|
|
3807
|
+
frac_in = _safe_input("Min relative peak height (0–1, default 0.1): ").strip()
|
|
3777
3808
|
min_frac = float(frac_in) if frac_in else 0.1
|
|
3778
3809
|
if min_frac < 0: min_frac = 0.0
|
|
3779
3810
|
if min_frac > 1: min_frac = 1.0
|
|
3780
3811
|
|
|
3781
|
-
swin =
|
|
3812
|
+
swin = _safe_input("Smoothing window (odd int >=3, blank=none): ").strip()
|
|
3782
3813
|
if swin:
|
|
3783
3814
|
try:
|
|
3784
3815
|
win = int(swin)
|
batplot/modes.py
CHANGED
|
@@ -280,8 +280,8 @@ def handle_cv_mode(args) -> int:
|
|
|
280
280
|
ax.set_xlabel('Current (mA)', labelpad=8.0)
|
|
281
281
|
ax.set_ylabel('Voltage (V)', labelpad=8.0)
|
|
282
282
|
else:
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
ax.set_xlabel('Voltage (V)', labelpad=8.0)
|
|
284
|
+
ax.set_ylabel('Current (mA)', labelpad=8.0)
|
|
285
285
|
legend = ax.legend(title='Cycle')
|
|
286
286
|
legend.get_title().set_fontsize('medium')
|
|
287
287
|
# Adjust margins to prevent label clipping
|
|
@@ -642,8 +642,8 @@ def handle_gc_mode(args) -> int:
|
|
|
642
642
|
ln_c, = ax.plot(y_b, x_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
643
643
|
linewidth=2.0, label=str(cyc), alpha=0.8)
|
|
644
644
|
else:
|
|
645
|
-
|
|
646
|
-
|
|
645
|
+
ln_c, = ax.plot(x_b, y_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
646
|
+
linewidth=2.0, label=str(cyc), alpha=0.8)
|
|
647
647
|
else:
|
|
648
648
|
ln_c = None
|
|
649
649
|
mask_d = (cyc_int == cyc) & discharge_mask
|
|
@@ -656,8 +656,8 @@ def handle_gc_mode(args) -> int:
|
|
|
656
656
|
ln_d, = ax.plot(yd_b, xd_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
657
657
|
linewidth=2.0, label=lbl, alpha=0.8)
|
|
658
658
|
else:
|
|
659
|
-
|
|
660
|
-
|
|
659
|
+
ln_d, = ax.plot(xd_b, yd_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
660
|
+
linewidth=2.0, label=lbl, alpha=0.8)
|
|
661
661
|
else:
|
|
662
662
|
ln_d = None
|
|
663
663
|
cycle_lines[cyc] = {"charge": ln_c, "discharge": ln_d}
|
|
@@ -677,8 +677,8 @@ def handle_gc_mode(args) -> int:
|
|
|
677
677
|
ln_c, = ax.plot(y_b, x_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
678
678
|
linewidth=2.0, label=str(cyc), alpha=0.8)
|
|
679
679
|
else:
|
|
680
|
-
|
|
681
|
-
|
|
680
|
+
ln_c, = ax.plot(x_b, y_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
681
|
+
linewidth=2.0, label=str(cyc), alpha=0.8)
|
|
682
682
|
ln_d = None
|
|
683
683
|
if i < len(dch_blocks):
|
|
684
684
|
a, b = dch_blocks[i]
|
|
@@ -690,8 +690,8 @@ def handle_gc_mode(args) -> int:
|
|
|
690
690
|
ln_d, = ax.plot(yd_b, xd_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
691
691
|
linewidth=2.0, label=lbl, alpha=0.8)
|
|
692
692
|
else:
|
|
693
|
-
|
|
694
|
-
|
|
693
|
+
ln_d, = ax.plot(xd_b, yd_b, '-', color=base_colors[(cyc-1) % len(base_colors)],
|
|
694
|
+
linewidth=2.0, label=lbl, alpha=0.8)
|
|
695
695
|
cycle_lines[cyc] = {"charge": ln_c, "discharge": ln_d}
|
|
696
696
|
|
|
697
697
|
# Swap x and y if --ro flag is set
|
|
@@ -699,8 +699,8 @@ def handle_gc_mode(args) -> int:
|
|
|
699
699
|
ax.set_xlabel('Voltage (V)', labelpad=8.0)
|
|
700
700
|
ax.set_ylabel(x_label_gc, labelpad=8.0)
|
|
701
701
|
else:
|
|
702
|
-
|
|
703
|
-
|
|
702
|
+
ax.set_xlabel(x_label_gc, labelpad=8.0)
|
|
703
|
+
ax.set_ylabel('Voltage (V)', labelpad=8.0)
|
|
704
704
|
legend = ax.legend(title='Cycle')
|
|
705
705
|
legend.get_title().set_fontsize('medium')
|
|
706
706
|
fig.subplots_adjust(left=0.12, right=0.95, top=0.88, bottom=0.15)
|