batplot 1.7.24__py3-none-any.whl → 1.7.26__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/batplot.py +64 -33
- batplot/color_utils.py +13 -6
- batplot/cpc_interactive.py +1274 -627
- batplot/electrochem_interactive.py +21 -3
- batplot/interactive.py +9 -5
- batplot/operando_ec_interactive.py +2 -2
- batplot/session.py +129 -17
- batplot/ui.py +13 -29
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/METADATA +1 -1
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/RECORD +15 -15
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/WHEEL +0 -0
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/entry_points.txt +0 -0
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.7.24.dist-info → batplot-1.7.26.dist-info}/top_level.txt +0 -0
|
@@ -1684,6 +1684,13 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1684
1684
|
if not already_confirmed and os.path.exists(target):
|
|
1685
1685
|
target = _confirm_overwrite(target)
|
|
1686
1686
|
if target:
|
|
1687
|
+
# Save current legend position before export (savefig can change layout)
|
|
1688
|
+
saved_legend_pos = None
|
|
1689
|
+
try:
|
|
1690
|
+
saved_legend_pos = getattr(fig, '_ec_legend_xy_in', None)
|
|
1691
|
+
except Exception:
|
|
1692
|
+
pass
|
|
1693
|
+
|
|
1687
1694
|
# If exporting SVG, make background transparent for PowerPoint
|
|
1688
1695
|
_, ext2 = os.path.splitext(target)
|
|
1689
1696
|
ext2 = ext2.lower()
|
|
@@ -1727,6 +1734,15 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
1727
1734
|
fig.savefig(target, bbox_inches='tight')
|
|
1728
1735
|
print(f"Exported figure to {target}")
|
|
1729
1736
|
fig._last_figure_export_path = target
|
|
1737
|
+
|
|
1738
|
+
# Restore legend position after savefig (which may have changed layout)
|
|
1739
|
+
if saved_legend_pos is not None:
|
|
1740
|
+
try:
|
|
1741
|
+
fig._ec_legend_xy_in = saved_legend_pos
|
|
1742
|
+
_rebuild_legend(ax)
|
|
1743
|
+
fig.canvas.draw_idle()
|
|
1744
|
+
except Exception:
|
|
1745
|
+
pass
|
|
1730
1746
|
except Exception as e:
|
|
1731
1747
|
print(f"Export failed: {e}")
|
|
1732
1748
|
except Exception as e:
|
|
@@ -2641,7 +2657,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
2641
2657
|
try:
|
|
2642
2658
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
2643
2659
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
2644
|
-
print(" Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2660
|
+
print(" Bullet: $\\bullet$ → • | Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2645
2661
|
while True:
|
|
2646
2662
|
print("Rename axis: x, y, both, q=back")
|
|
2647
2663
|
sub = input("Rename> ").strip().lower()
|
|
@@ -3283,8 +3299,10 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3283
3299
|
continue
|
|
3284
3300
|
elif key == 'f':
|
|
3285
3301
|
# Font submenu with numbered options
|
|
3302
|
+
cur_family = plt.rcParams.get('font.sans-serif', [''])[0]
|
|
3303
|
+
cur_size = plt.rcParams.get('font.size', None)
|
|
3286
3304
|
while True:
|
|
3287
|
-
print("\nFont menu: f=font family, s=size, q=back")
|
|
3305
|
+
print(f"\nFont menu (current: family='{cur_family}', size={cur_size}): f=font family, s=size, q=back")
|
|
3288
3306
|
sub = input("Font> ").strip().lower()
|
|
3289
3307
|
if not sub:
|
|
3290
3308
|
continue
|
|
@@ -3298,7 +3316,7 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Dict[int, Dict[str, Optio
|
|
|
3298
3316
|
for i, font in enumerate(fonts, 1):
|
|
3299
3317
|
print(f" {i}: {font}")
|
|
3300
3318
|
print("Or enter custom font name directly.")
|
|
3301
|
-
choice = input("Font family (number or name): ").strip()
|
|
3319
|
+
choice = input(f"Font family (current: '{cur_family}', number or name): ").strip()
|
|
3302
3320
|
if not choice:
|
|
3303
3321
|
continue
|
|
3304
3322
|
# Check if it's a number
|
batplot/interactive.py
CHANGED
|
@@ -963,7 +963,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
963
963
|
txt = ax.text(1.0, 1.0, "",
|
|
964
964
|
ha='right', va='bottom',
|
|
965
965
|
transform=ax.transAxes,
|
|
966
|
-
fontsize=max(9, int(0.6 * plt.rcParams.get('font.size',
|
|
966
|
+
fontsize=max(9, int(0.6 * plt.rcParams.get('font.size', 16))),
|
|
967
967
|
color='0.15',
|
|
968
968
|
bbox=dict(boxstyle='round,pad=0.25', fc='white', ec='0.7', alpha=0.8))
|
|
969
969
|
|
|
@@ -2179,7 +2179,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2179
2179
|
if mode == 'c':
|
|
2180
2180
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
2181
2181
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
2182
|
-
print(" Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2182
|
+
print(" Bullet: $\\bullet$ → • | Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
2183
2183
|
idx_in = input("Curve number to rename (q=cancel): ").strip()
|
|
2184
2184
|
if not idx_in or idx_in.lower() == 'q':
|
|
2185
2185
|
print("Canceled.")
|
|
@@ -3130,15 +3130,18 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3130
3130
|
except Exception as e:
|
|
3131
3131
|
print(f"Error setting widths: {e}")
|
|
3132
3132
|
elif key == 'f':
|
|
3133
|
+
cur_family = plt.rcParams.get('font.sans-serif', [''])[0]
|
|
3134
|
+
cur_size = plt.rcParams.get('font.size', None)
|
|
3133
3135
|
while True:
|
|
3134
|
-
subkey = input(colorize_prompt("Font submenu (s=size, f=family, q=return
|
|
3136
|
+
subkey = input(colorize_prompt(f"Font submenu (current: family='{cur_family}', size={cur_size}) - s=size, f=family, q=return: ")).strip().lower()
|
|
3135
3137
|
if subkey == 'q':
|
|
3136
3138
|
break
|
|
3137
3139
|
if subkey == '':
|
|
3138
3140
|
continue
|
|
3139
3141
|
if subkey == 's':
|
|
3140
3142
|
try:
|
|
3141
|
-
|
|
3143
|
+
cur_size = plt.rcParams.get('font.size', None)
|
|
3144
|
+
fs = input(f"Enter new font size (current: {cur_size}, q=cancel): ").strip()
|
|
3142
3145
|
if not fs or fs.lower() == 'q':
|
|
3143
3146
|
print("Canceled.")
|
|
3144
3147
|
else:
|
|
@@ -3153,13 +3156,14 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3153
3156
|
print(f"Error changing font size: {e}")
|
|
3154
3157
|
elif subkey == 'f':
|
|
3155
3158
|
try:
|
|
3159
|
+
cur_family = plt.rcParams.get('font.sans-serif', [''])[0]
|
|
3156
3160
|
print("Common publication fonts:")
|
|
3157
3161
|
print(" 1) Arial")
|
|
3158
3162
|
print(" 2) Helvetica")
|
|
3159
3163
|
print(" 3) Times New Roman")
|
|
3160
3164
|
print(" 4) STIXGeneral")
|
|
3161
3165
|
print(" 5) DejaVu Sans")
|
|
3162
|
-
ft_raw = input("Enter font number or family name (q=cancel): ").strip()
|
|
3166
|
+
ft_raw = input(f"Enter font number or family name (current: '{cur_family}', q=cancel): ").strip()
|
|
3163
3167
|
if not ft_raw or ft_raw.lower() == 'q':
|
|
3164
3168
|
print("Canceled.")
|
|
3165
3169
|
else:
|
|
@@ -1553,7 +1553,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1553
1553
|
# Create text annotations for coordinates
|
|
1554
1554
|
coord_text = fig.text(0.02, 0.98, '', transform=fig.transFigure,
|
|
1555
1555
|
verticalalignment='top',
|
|
1556
|
-
fontsize=max(9, int(0.6 * mpl_plt.rcParams.get('font.size',
|
|
1556
|
+
fontsize=max(9, int(0.6 * mpl_plt.rcParams.get('font.size', 16))),
|
|
1557
1557
|
color='0.15',
|
|
1558
1558
|
bbox=dict(boxstyle='round,pad=0.25', fc='white', ec='0.7', alpha=0.8))
|
|
1559
1559
|
except Exception as e:
|
|
@@ -4465,7 +4465,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
4465
4465
|
print("Rename Operando Axes: x=rename X label, y=rename Y label, q=back")
|
|
4466
4466
|
print("Tip: Use LaTeX/mathtext for special characters:")
|
|
4467
4467
|
print(" Subscript: H$_2$O → H₂O | Superscript: m$^2$ → m²")
|
|
4468
|
-
print(" Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
4468
|
+
print(" Bullet: $\\bullet$ → • | Greek: $\\alpha$, $\\beta$ | Angstrom: $\\AA$ → Å")
|
|
4469
4469
|
while True:
|
|
4470
4470
|
sub = input("or> ").strip().lower()
|
|
4471
4471
|
if not sub:
|
batplot/session.py
CHANGED
|
@@ -2408,6 +2408,30 @@ def dump_cpc_session(
|
|
|
2408
2408
|
if file_data and isinstance(file_data, list) and len(file_data) > 0:
|
|
2409
2409
|
multi_files = []
|
|
2410
2410
|
for f in file_data:
|
|
2411
|
+
def _marker_of(sc, default_val):
|
|
2412
|
+
try:
|
|
2413
|
+
m = getattr(sc, 'get_marker', lambda: default_val)()
|
|
2414
|
+
if m is None:
|
|
2415
|
+
return default_val
|
|
2416
|
+
return m
|
|
2417
|
+
except Exception:
|
|
2418
|
+
return default_val
|
|
2419
|
+
def _alpha_of(sc, default_val=None):
|
|
2420
|
+
try:
|
|
2421
|
+
a = sc.get_alpha()
|
|
2422
|
+
return float(a) if a is not None else default_val
|
|
2423
|
+
except Exception:
|
|
2424
|
+
return default_val
|
|
2425
|
+
def _visible_of(sc, default_val=True):
|
|
2426
|
+
try:
|
|
2427
|
+
return bool(sc.get_visible())
|
|
2428
|
+
except Exception:
|
|
2429
|
+
return default_val
|
|
2430
|
+
def _label_of(sc, default_val=""):
|
|
2431
|
+
try:
|
|
2432
|
+
return sc.get_label() or default_val
|
|
2433
|
+
except Exception:
|
|
2434
|
+
return default_val
|
|
2411
2435
|
file_info = {
|
|
2412
2436
|
'filename': f.get('filename', 'unknown'),
|
|
2413
2437
|
'visible': f.get('visible', True),
|
|
@@ -2415,16 +2439,31 @@ def dump_cpc_session(
|
|
|
2415
2439
|
'x': _np.array(_scatter_xy(f.get('sc_charge', sc_charge))[0]),
|
|
2416
2440
|
'y': _np.array(_scatter_xy(f.get('sc_charge', sc_charge))[1]),
|
|
2417
2441
|
'color': _color_of(f.get('sc_charge')),
|
|
2442
|
+
'size': _size_of(f.get('sc_charge'), 32.0),
|
|
2443
|
+
'alpha': _alpha_of(f.get('sc_charge')),
|
|
2444
|
+
'marker': _marker_of(f.get('sc_charge'), 'o'),
|
|
2445
|
+
'label': _label_of(f.get('sc_charge'), 'Charge capacity'),
|
|
2446
|
+
'visible': _visible_of(f.get('sc_charge')),
|
|
2418
2447
|
},
|
|
2419
2448
|
'discharge': {
|
|
2420
2449
|
'x': _np.array(_scatter_xy(f.get('sc_discharge', sc_discharge))[0]),
|
|
2421
2450
|
'y': _np.array(_scatter_xy(f.get('sc_discharge', sc_discharge))[1]),
|
|
2422
2451
|
'color': _color_of(f.get('sc_discharge')),
|
|
2452
|
+
'size': _size_of(f.get('sc_discharge'), 32.0),
|
|
2453
|
+
'alpha': _alpha_of(f.get('sc_discharge')),
|
|
2454
|
+
'marker': _marker_of(f.get('sc_discharge'), 's'),
|
|
2455
|
+
'label': _label_of(f.get('sc_discharge'), 'Discharge capacity'),
|
|
2456
|
+
'visible': _visible_of(f.get('sc_discharge')),
|
|
2423
2457
|
},
|
|
2424
2458
|
'efficiency': {
|
|
2425
2459
|
'x': _np.array(_scatter_xy(f.get('sc_eff', sc_eff))[0]),
|
|
2426
2460
|
'y': _np.array(_scatter_xy(f.get('sc_eff', sc_eff))[1]),
|
|
2427
2461
|
'color': _color_of(f.get('sc_eff')),
|
|
2462
|
+
'size': _size_of(f.get('sc_eff'), 40.0),
|
|
2463
|
+
'alpha': _alpha_of(f.get('sc_eff')),
|
|
2464
|
+
'marker': _marker_of(f.get('sc_eff'), '^'),
|
|
2465
|
+
'label': _label_of(f.get('sc_eff'), 'Coulombic efficiency'),
|
|
2466
|
+
'visible': _visible_of(f.get('sc_eff')),
|
|
2428
2467
|
}
|
|
2429
2468
|
}
|
|
2430
2469
|
multi_files.append(file_info)
|
|
@@ -2445,9 +2484,9 @@ def dump_cpc_session(
|
|
|
2445
2484
|
|
|
2446
2485
|
|
|
2447
2486
|
def load_cpc_session(filename: str):
|
|
2448
|
-
"""Load a CPC session and reconstruct fig, axes,
|
|
2487
|
+
"""Load a CPC session and reconstruct fig, axes, scatter artists, and file_data.
|
|
2449
2488
|
|
|
2450
|
-
Returns: (fig, ax, ax2, sc_charge, sc_discharge, sc_eff)
|
|
2489
|
+
Returns: (fig, ax, ax2, sc_charge, sc_discharge, sc_eff, file_data)
|
|
2451
2490
|
"""
|
|
2452
2491
|
try:
|
|
2453
2492
|
with open(filename, 'rb') as f:
|
|
@@ -2534,12 +2573,47 @@ def load_cpc_session(filename: str):
|
|
|
2534
2573
|
except Exception:
|
|
2535
2574
|
pass
|
|
2536
2575
|
return sc
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
if
|
|
2541
|
-
|
|
2542
|
-
|
|
2576
|
+
# If multi_files exist, rebuild all files and pick the first as primary
|
|
2577
|
+
multi_files = sess.get('multi_files')
|
|
2578
|
+
file_data = []
|
|
2579
|
+
if multi_files and isinstance(multi_files, list) and len(multi_files) > 0:
|
|
2580
|
+
for idx, finfo in enumerate(multi_files):
|
|
2581
|
+
ch_info = finfo.get('charge', {})
|
|
2582
|
+
dh_info = finfo.get('discharge', {})
|
|
2583
|
+
ef_info = finfo.get('efficiency', {})
|
|
2584
|
+
sc_ch = _mk_sc(ax, ch_info, ch_info.get('marker', 'o') or 'o')
|
|
2585
|
+
sc_dh = _mk_sc(ax, dh_info, dh_info.get('marker', 's') or 's')
|
|
2586
|
+
eff_marker = ef_info.get('marker', '^') or '^'
|
|
2587
|
+
sc_ef = _mk_sc(ax2, ef_info, eff_marker)
|
|
2588
|
+
# Respect overall file visibility
|
|
2589
|
+
try:
|
|
2590
|
+
vis_file = bool(finfo.get('visible', True))
|
|
2591
|
+
except Exception:
|
|
2592
|
+
vis_file = True
|
|
2593
|
+
for sc_tmp in (sc_ch, sc_dh, sc_ef):
|
|
2594
|
+
try:
|
|
2595
|
+
sc_tmp.set_visible(sc_tmp.get_visible() and vis_file)
|
|
2596
|
+
except Exception:
|
|
2597
|
+
pass
|
|
2598
|
+
file_data.append({
|
|
2599
|
+
'filename': finfo.get('filename', f'File {idx+1}'),
|
|
2600
|
+
'visible': vis_file,
|
|
2601
|
+
'sc_charge': sc_ch,
|
|
2602
|
+
'sc_discharge': sc_dh,
|
|
2603
|
+
'sc_eff': sc_ef,
|
|
2604
|
+
})
|
|
2605
|
+
# Use the first file as primary artists for interactive menu
|
|
2606
|
+
sc_charge = file_data[0]['sc_charge']
|
|
2607
|
+
sc_discharge = file_data[0]['sc_discharge']
|
|
2608
|
+
sc_eff = file_data[0]['sc_eff']
|
|
2609
|
+
else:
|
|
2610
|
+
# No multi-file info: fall back to single-file series
|
|
2611
|
+
sc_charge = _mk_sc(ax, ch, 'o')
|
|
2612
|
+
sc_discharge = _mk_sc(ax, dh, 's')
|
|
2613
|
+
if 'marker' not in ef:
|
|
2614
|
+
ef['marker'] = '^'
|
|
2615
|
+
sc_eff = _mk_sc(ax2, ef, '^')
|
|
2616
|
+
file_data = None
|
|
2543
2617
|
|
|
2544
2618
|
# Restore spines state (version 2+)
|
|
2545
2619
|
try:
|
|
@@ -2693,6 +2767,16 @@ def load_cpc_session(filename: str):
|
|
|
2693
2767
|
ax2.tick_params(axis='y',
|
|
2694
2768
|
right=wasd_state['right'].get('ticks', True),
|
|
2695
2769
|
labelright=wasd_state['right'].get('labels', True))
|
|
2770
|
+
# Axis title visibility
|
|
2771
|
+
try:
|
|
2772
|
+
if 'bottom' in wasd_state:
|
|
2773
|
+
ax.xaxis.label.set_visible(bool(wasd_state['bottom'].get('title', True)))
|
|
2774
|
+
if 'left' in wasd_state:
|
|
2775
|
+
ax.yaxis.label.set_visible(bool(wasd_state['left'].get('title', True)))
|
|
2776
|
+
if 'right' in wasd_state:
|
|
2777
|
+
ax2.yaxis.label.set_visible(bool(wasd_state['right'].get('title', True)))
|
|
2778
|
+
except Exception:
|
|
2779
|
+
pass
|
|
2696
2780
|
|
|
2697
2781
|
# Minor ticks
|
|
2698
2782
|
if wasd_state.get('top', {}).get('minor') or wasd_state.get('bottom', {}).get('minor'):
|
|
@@ -2767,26 +2851,54 @@ def load_cpc_session(filename: str):
|
|
|
2767
2851
|
try:
|
|
2768
2852
|
handles1, labels1 = ax.get_legend_handles_labels()
|
|
2769
2853
|
handles2, labels2 = ax2.get_legend_handles_labels()
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2854
|
+
# Filter visible handles only
|
|
2855
|
+
H, L = [], []
|
|
2856
|
+
for h, l in list(zip(handles1, labels1)) + list(zip(handles2, labels2)):
|
|
2857
|
+
try:
|
|
2858
|
+
if hasattr(h, 'get_visible') and not h.get_visible():
|
|
2859
|
+
continue
|
|
2860
|
+
except Exception:
|
|
2861
|
+
pass
|
|
2862
|
+
H.append(h); L.append(l)
|
|
2863
|
+
leg_meta = sess.get('legend', {})
|
|
2864
|
+
xy_in = leg_meta.get('xy_in')
|
|
2865
|
+
vis = bool(leg_meta.get('visible', True))
|
|
2866
|
+
if H and vis:
|
|
2773
2867
|
if xy_in is not None:
|
|
2774
2868
|
fw, fh = fig.get_size_inches()
|
|
2775
2869
|
fx = 0.5 + float(xy_in[0]) / float(fw)
|
|
2776
2870
|
fy = 0.5 + float(xy_in[1]) / float(fh)
|
|
2777
|
-
|
|
2871
|
+
# Use same spacing parameters as _legend_no_frame for consistent legend appearance
|
|
2872
|
+
leg = ax.legend(H, L, loc='center', bbox_to_anchor=(fx, fy), bbox_transform=fig.transFigure,
|
|
2873
|
+
handlelength=1.0, handletextpad=0.35, labelspacing=0.25,
|
|
2874
|
+
borderaxespad=0.5, borderpad=0.3, columnspacing=0.6,
|
|
2875
|
+
labelcolor='linecolor', frameon=False)
|
|
2778
2876
|
# persist inches on fig for interactive menu
|
|
2779
2877
|
try:
|
|
2780
2878
|
fig._cpc_legend_xy_in = (float(xy_in[0]), float(xy_in[1]))
|
|
2781
2879
|
except Exception:
|
|
2782
2880
|
pass
|
|
2783
2881
|
else:
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2882
|
+
# Use same spacing parameters as _legend_no_frame for consistent legend appearance
|
|
2883
|
+
leg = ax.legend(H, L, loc='best',
|
|
2884
|
+
handlelength=1.0, handletextpad=0.35, labelspacing=0.25,
|
|
2885
|
+
borderaxespad=0.5, borderpad=0.3, columnspacing=0.6,
|
|
2886
|
+
labelcolor='linecolor', frameon=False)
|
|
2887
|
+
# Ensure legend frame is off (redundant but safe)
|
|
2888
|
+
try:
|
|
2889
|
+
if leg is not None:
|
|
2890
|
+
leg.set_frame_on(False)
|
|
2891
|
+
except Exception:
|
|
2892
|
+
pass
|
|
2893
|
+
else:
|
|
2894
|
+
try:
|
|
2895
|
+
fig._cpc_legend_xy_in = (float(xy_in[0]), float(xy_in[1])) if xy_in is not None else None
|
|
2896
|
+
except Exception:
|
|
2897
|
+
pass
|
|
2898
|
+
# ensure legend hidden
|
|
2787
2899
|
leg = ax.get_legend()
|
|
2788
2900
|
if leg is not None:
|
|
2789
|
-
leg.set_visible(
|
|
2901
|
+
leg.set_visible(False)
|
|
2790
2902
|
except Exception:
|
|
2791
2903
|
pass
|
|
2792
2904
|
try:
|
|
@@ -2796,7 +2908,7 @@ def load_cpc_session(filename: str):
|
|
|
2796
2908
|
fig.canvas.draw_idle()
|
|
2797
2909
|
except Exception:
|
|
2798
2910
|
pass
|
|
2799
|
-
return fig, ax, ax2, sc_charge, sc_discharge, sc_eff
|
|
2911
|
+
return fig, ax, ax2, sc_charge, sc_discharge, sc_eff, file_data
|
|
2800
2912
|
except Exception as e:
|
|
2801
2913
|
import traceback
|
|
2802
2914
|
print(f"Error loading CPC session: {e}")
|
batplot/ui.py
CHANGED
|
@@ -611,7 +611,7 @@ def resize_plot_frame(fig, ax, y_data_list: List, label_text_objects: List, args
|
|
|
611
611
|
ax_bbox = ax.get_position()
|
|
612
612
|
cur_ax_w_in = ax_bbox.width * fig_w_in
|
|
613
613
|
cur_ax_h_in = ax_bbox.height * fig_h_in
|
|
614
|
-
print(f"Current canvas
|
|
614
|
+
print(f"Current canvas: {fig_w_in:.2f} x {fig_h_in:.2f} in")
|
|
615
615
|
print(f"Current plot frame: {cur_ax_w_in:.2f} x {cur_ax_h_in:.2f} in (W x H)")
|
|
616
616
|
try:
|
|
617
617
|
spec = input("Enter new plot frame size (e.g. '6 4', '6x4', 'w=6 h=4', 'scale=1.2', single width, q=back): ").strip().lower()
|
|
@@ -651,16 +651,9 @@ def resize_plot_frame(fig, ax, y_data_list: List, label_text_objects: List, args
|
|
|
651
651
|
print("Could not parse specification.")
|
|
652
652
|
continue
|
|
653
653
|
req_w_in, req_h_in = new_w_in, new_h_in
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if new_w_in > max_w_in:
|
|
658
|
-
print(f"Requested width {new_w_in:.2f} exceeds max {max_w_in:.2f}; clamped.")
|
|
659
|
-
new_w_in = max_w_in
|
|
660
|
-
if new_h_in > max_h_in:
|
|
661
|
-
print(f"Requested height {new_h_in:.2f} exceeds max {max_h_in:.2f}; clamped.")
|
|
662
|
-
new_h_in = max_h_in
|
|
663
|
-
min_ax_in = 0.25
|
|
654
|
+
# Apply exact requested size without any clamping
|
|
655
|
+
# Only enforce minimum size to prevent division by zero
|
|
656
|
+
min_ax_in = 0.01
|
|
664
657
|
new_w_in = max(min_ax_in, new_w_in)
|
|
665
658
|
new_h_in = max(min_ax_in, new_h_in)
|
|
666
659
|
tol = 1e-3
|
|
@@ -676,33 +669,24 @@ def resize_plot_frame(fig, ax, y_data_list: List, label_text_objects: List, args
|
|
|
676
669
|
lm, bm, rm, tm = fig._last_user_margins
|
|
677
670
|
fig.subplots_adjust(left=lm, bottom=bm, right=rm, top=tm)
|
|
678
671
|
update_labels_func(ax, y_data_list, label_text_objects, args.stack)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
continue
|
|
672
|
+
fig.canvas.draw_idle()
|
|
673
|
+
print(f"Plot frame unchanged ({new_w_in:.2f} x {new_h_in:.2f} in); layout preserved.")
|
|
674
|
+
continue
|
|
683
675
|
left = (1 - w_frac) / 2
|
|
684
676
|
right = left + w_frac
|
|
685
677
|
bottom = (1 - h_frac) / 2
|
|
686
678
|
top = bottom + h_frac
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
right = min(1 - min_margin_frac, right)
|
|
690
|
-
top = min(1 - min_margin_frac, top)
|
|
691
|
-
if right - left < 0.05 or top - bottom < 0.05:
|
|
692
|
-
print("Requested frame too small after safety clamps; aborting.")
|
|
693
|
-
else:
|
|
694
|
-
fig.subplots_adjust(left=left, right=right, bottom=bottom, top=top)
|
|
679
|
+
# Apply exact size without margin clamping
|
|
680
|
+
fig.subplots_adjust(left=left, right=right, bottom=bottom, top=top)
|
|
695
681
|
update_labels_func(ax, y_data_list, label_text_objects, args.stack)
|
|
696
|
-
|
|
682
|
+
# Store the final size (exactly as requested, no text visibility adjustments)
|
|
697
683
|
sp = fig.subplotpars
|
|
698
|
-
fig._last_user_axes_inches = (
|
|
684
|
+
fig._last_user_axes_inches = (new_w_in, new_h_in)
|
|
699
685
|
fig._last_user_margins = (sp.left, sp.bottom, sp.right, sp.top)
|
|
700
686
|
final_w_in = (sp.right - sp.left) * fig_w_in
|
|
701
687
|
final_h_in = (sp.top - sp.bottom) * fig_h_in
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
else:
|
|
705
|
-
print(f"Plot frame set to {final_w_in:.2f} x {final_h_in:.2f} in inside fixed canvas {fig_w_in:.2f} x {fig_h_in:.2f} in.")
|
|
688
|
+
# Show the requested size (which is what was applied)
|
|
689
|
+
print(f"Plot frame set to {req_w_in:.2f} x {req_h_in:.2f} in inside canvas {fig_w_in:.2f} x {fig_h_in:.2f} in.")
|
|
706
690
|
except KeyboardInterrupt:
|
|
707
691
|
print("Canceled.")
|
|
708
692
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.26
|
|
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
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
batplot/__init__.py,sha256=
|
|
1
|
+
batplot/__init__.py,sha256=l-iAn_Q1KA7sJv2vOZzjjw4K2UWfntwGpQk9Z3lyuRw,119
|
|
2
2
|
batplot/args.py,sha256=6g1eHVsycDSAenwrXuCTcyuXCy_-30zy1lWtdguBG2s,34983
|
|
3
3
|
batplot/batch.py,sha256=YQ7obCIqLCObwDbM7TXpOBh7g7BO95wZNsa2Fy84c6o,53858
|
|
4
|
-
batplot/batplot.py,sha256=
|
|
4
|
+
batplot/batplot.py,sha256=40lU1nY1NqeAOpzNG_vLF_L34COKhiA19pMpbvA3SJc,171885
|
|
5
5
|
batplot/cif.py,sha256=JfHwNf3SHrcpALc_F5NjJmQ3lg71MBRSaIUJjGYPTx8,30120
|
|
6
6
|
batplot/cli.py,sha256=ScDb2je8VQ0mz_z0SLCHEigiTuFPY5pb1snnzCouKms,5828
|
|
7
|
-
batplot/color_utils.py,sha256=
|
|
7
|
+
batplot/color_utils.py,sha256=7InQLVo1XTg7sgAbltM2KeDSFJgr787YEaV9vJbIoWY,20460
|
|
8
8
|
batplot/config.py,sha256=6nGY7fKN4T5KZUGQS2ArUBgEkLAL0j37XwG5SCVQgKA,6420
|
|
9
9
|
batplot/converters.py,sha256=rR2WMPM0nR5E3eZI3gWbaJf_AfbdQx3urVSbJmZXNzo,8237
|
|
10
|
-
batplot/cpc_interactive.py,sha256=
|
|
11
|
-
batplot/electrochem_interactive.py,sha256=
|
|
12
|
-
batplot/interactive.py,sha256=
|
|
10
|
+
batplot/cpc_interactive.py,sha256=YGt-TLfO5W_YyUdIInfW6y6bZxQJyfeGX7JGBv1kNbs,232634
|
|
11
|
+
batplot/electrochem_interactive.py,sha256=n_Jc9shH4LIL3r6z3MXnx02ZddMKZoh4dg25hHhfJRw,218297
|
|
12
|
+
batplot/interactive.py,sha256=johjaK8Er1y3rc5Cvg0C3Lv8Cqf2IliOyYrSnSrCzW4,203997
|
|
13
13
|
batplot/manual.py,sha256=pbRI6G4Pm12pOW8LrOLWWu7IEOtqWN3tRHtgge50LlA,11556
|
|
14
14
|
batplot/modes.py,sha256=qE2OsOQQKhwOWene5zxJeuuewTrZxubtahQuz5je7ok,37252
|
|
15
15
|
batplot/operando.py,sha256=CdTZJa6Cr1wNczFEbwAido2mc7C_h1xxoQ5b045ktSk,28105
|
|
16
|
-
batplot/operando_ec_interactive.py,sha256=
|
|
16
|
+
batplot/operando_ec_interactive.py,sha256=BOFj1E7H7b1wW0_pNwYCf7e2E70HemYzl1UjHihxB68,293096
|
|
17
17
|
batplot/plotting.py,sha256=hG2_EdDhF1Qpn1XfZKdCQ5-w_m9gUYFbr804UQ5QjsU,10841
|
|
18
18
|
batplot/readers.py,sha256=kAI0AvYrdfGRZkvADJ4riN96IWtrH24aAoZpBtONTbw,112960
|
|
19
|
-
batplot/session.py,sha256=
|
|
19
|
+
batplot/session.py,sha256=FEWYHu1XUIrWxD3an5m7HB57pHst2CHgJ_brAmCBk38,132199
|
|
20
20
|
batplot/style.py,sha256=ZeRJ15fneSQqCYN_SGap-9Y2J8UkzRxYmf6qLJA6zMo,62058
|
|
21
|
-
batplot/ui.py,sha256=
|
|
21
|
+
batplot/ui.py,sha256=ifpbK74juUzLMCt-sJGVaWtpDb1NMRJzs2YyiwwafzY,35302
|
|
22
22
|
batplot/utils.py,sha256=3dBZALWiCu5c6uc5MBII7n8329BZjieTEw4qithTlow,33939
|
|
23
23
|
batplot/version_check.py,sha256=OG4LuHo5-rSqLLHQo5nWbX9lbNq6NyxRdvVUUcJRBqQ,6219
|
|
24
24
|
batplot/data/USER_MANUAL.md,sha256=VYPvNZt3Fy8Z4Izr2FnQBw9vEaFTPkybhHDnF-OuKws,17694
|
|
25
|
-
batplot-1.7.
|
|
25
|
+
batplot-1.7.26.dist-info/licenses/LICENSE,sha256=2PAnHeCiTfgI7aKZLWr0G56HI9fGKQ0CEbQ02H-yExQ,1065
|
|
26
26
|
batplot_backup_20251121_223043/__init__.py,sha256=3s2DUQuTbWs65hoN9cQQ8IiJbaFJY8fNxiCpwRBYoOA,118
|
|
27
27
|
batplot_backup_20251121_223043/args.py,sha256=OH-h84QhN-IhMS8sPAsSEqccHD3wpeMgmXa_fqv5xtg,21215
|
|
28
28
|
batplot_backup_20251121_223043/batch.py,sha256=oI7PONJyciHDOqNPq-8fnOQMyn9CpAdVznKaEdsy0ig,48650
|
|
@@ -45,8 +45,8 @@ batplot_backup_20251121_223043/style.py,sha256=xg-tj6bEbFUVjjxYMokiLehS4tSfKanLI
|
|
|
45
45
|
batplot_backup_20251121_223043/ui.py,sha256=K0XZWyiuBRNkFod9mgZyJ9CLN78GR1-hh6EznnIb5S8,31208
|
|
46
46
|
batplot_backup_20251121_223043/utils.py,sha256=jydA0JxsCWWAudXEwSjlxTG17y2F8U6hIAukAzi1P0g,32526
|
|
47
47
|
batplot_backup_20251121_223043/version_check.py,sha256=vlHkGkgUJcD_Z4KZmwonxZvKZh0MwHLaBSxaLPc66AQ,4555
|
|
48
|
-
batplot-1.7.
|
|
49
|
-
batplot-1.7.
|
|
50
|
-
batplot-1.7.
|
|
51
|
-
batplot-1.7.
|
|
52
|
-
batplot-1.7.
|
|
48
|
+
batplot-1.7.26.dist-info/METADATA,sha256=nuvFZX39gTf0X-Dxff9jzQur4tLBTpRPuE55CPiYEdA,7407
|
|
49
|
+
batplot-1.7.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
50
|
+
batplot-1.7.26.dist-info/entry_points.txt,sha256=73GgH3Zs-qGIvgiyQLgGsSW-ryOwPPKHveOW6TDIR5Q,82
|
|
51
|
+
batplot-1.7.26.dist-info/top_level.txt,sha256=CgqK4RpsYnUFAcqO4bLOnEhCoPY4IPEGLPkiDlzLIxg,39
|
|
52
|
+
batplot-1.7.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|