batplot 1.7.22__py3-none-any.whl → 1.7.24__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/args.py +1 -0
- batplot/cpc_interactive.py +168 -11
- batplot/electrochem_interactive.py +152 -14
- batplot/interactive.py +156 -30
- batplot/operando_ec_interactive.py +198 -21
- batplot/session.py +92 -1
- batplot/style.py +109 -47
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/METADATA +26 -3
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/RECORD +14 -14
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/WHEEL +0 -0
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/entry_points.txt +0 -0
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.7.22.dist-info → batplot-1.7.24.dist-info}/top_level.txt +0 -0
|
@@ -1050,6 +1050,15 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1050
1050
|
# Horizontal offsets (relative to canvas center, in inches)
|
|
1051
1051
|
cb_h_offset = getattr(cbar.ax, '_cb_h_offset_in', 0.0)
|
|
1052
1052
|
ec_h_offset = getattr(ec_ax, '_ec_h_offset_in', 0.0) if ec_ax is not None else None
|
|
1053
|
+
# Colorbar tick/label positions (left/right)
|
|
1054
|
+
cb_ticks_left = True
|
|
1055
|
+
cb_label_left = True
|
|
1056
|
+
try:
|
|
1057
|
+
cb_ticks_left = any(getattr(tick, 'tick1line', None) and tick.tick1line.get_visible() for tick in cbar.ax.yaxis.get_major_ticks())
|
|
1058
|
+
# label position is stored on axis; capture current setting
|
|
1059
|
+
cb_label_left = (cbar.ax.yaxis.get_label_position() == 'left')
|
|
1060
|
+
except Exception:
|
|
1061
|
+
pass
|
|
1053
1062
|
# Label pads (save current labelpad values to restore later)
|
|
1054
1063
|
op_labelpads = {
|
|
1055
1064
|
'x': getattr(ax.xaxis, 'labelpad', None),
|
|
@@ -1084,6 +1093,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1084
1093
|
'ec_visible': ec_visible,
|
|
1085
1094
|
'cb_h_offset': float(cb_h_offset),
|
|
1086
1095
|
'ec_h_offset': float(ec_h_offset) if ec_h_offset is not None else None,
|
|
1096
|
+
'cb_ticks_left': cb_ticks_left,
|
|
1097
|
+
'cb_label_left': cb_label_left,
|
|
1087
1098
|
'op_labelpads': dict(op_labelpads),
|
|
1088
1099
|
'ec_labelpads': dict(ec_labelpads) if ec_labelpads is not None else None,
|
|
1089
1100
|
'op_title_offsets': {
|
|
@@ -1138,6 +1149,14 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1138
1149
|
_apply_group_layout_inches(fig, ax, cbar.ax, ec_ax, ax_w_i, ax_h_i, cb_w_i, cb_gap_i, ec_gap_i, ec_w_i)
|
|
1139
1150
|
except Exception:
|
|
1140
1151
|
pass
|
|
1152
|
+
# Colorbar tick/label side
|
|
1153
|
+
try:
|
|
1154
|
+
cb_ticks_left = snap.get('cb_ticks_left', True)
|
|
1155
|
+
cb_label_left = snap.get('cb_label_left', True)
|
|
1156
|
+
cbar.ax.yaxis.set_ticks_position('left' if cb_ticks_left else 'right')
|
|
1157
|
+
cbar.ax.yaxis.set_label_position('left' if cb_label_left else 'right')
|
|
1158
|
+
except Exception:
|
|
1159
|
+
pass
|
|
1141
1160
|
# Labels
|
|
1142
1161
|
try:
|
|
1143
1162
|
op_l = snap.get('op_labels', {})
|
|
@@ -1170,6 +1189,14 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1170
1189
|
pass
|
|
1171
1190
|
try:
|
|
1172
1191
|
if snap.get('clim') is not None:
|
|
1192
|
+
# Detach built-in colorbar update to avoid artist removal errors; we redraw custom below.
|
|
1193
|
+
try:
|
|
1194
|
+
if hasattr(cbar, 'mappable'):
|
|
1195
|
+
cbar.mappable = None
|
|
1196
|
+
if hasattr(cbar, 'solids'):
|
|
1197
|
+
cbar.solids = None
|
|
1198
|
+
except Exception:
|
|
1199
|
+
pass
|
|
1173
1200
|
lo, hi = snap['clim']; im.set_clim(float(lo), float(hi))
|
|
1174
1201
|
except Exception:
|
|
1175
1202
|
pass
|
|
@@ -1180,6 +1207,15 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1180
1207
|
cbar.update_normal(im)
|
|
1181
1208
|
except Exception:
|
|
1182
1209
|
pass
|
|
1210
|
+
# Restore colorbar side (ticks/label) and redraw custom colorbar to keep position
|
|
1211
|
+
try:
|
|
1212
|
+
cb_ticks_left = snap.get('cb_ticks_left', True)
|
|
1213
|
+
cb_label_left = snap.get('cb_label_left', True)
|
|
1214
|
+
cbar.ax.yaxis.set_ticks_position('left' if cb_ticks_left else 'right')
|
|
1215
|
+
cbar.ax.yaxis.set_label_position('left' if cb_label_left else 'right')
|
|
1216
|
+
_update_custom_colorbar(cbar.ax, im)
|
|
1217
|
+
except Exception:
|
|
1218
|
+
pass
|
|
1183
1219
|
# EC axes
|
|
1184
1220
|
try:
|
|
1185
1221
|
if ec_ax is not None:
|
|
@@ -1680,13 +1716,31 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1680
1716
|
else:
|
|
1681
1717
|
print(f" {i}: {fname}")
|
|
1682
1718
|
|
|
1683
|
-
|
|
1719
|
+
last_figure_path = getattr(fig, '_last_figure_export_path', None)
|
|
1720
|
+
if last_figure_path:
|
|
1721
|
+
fname = input("Export filename (default .svg if no extension), number to overwrite, or o to overwrite last (q=cancel): ").strip()
|
|
1722
|
+
else:
|
|
1723
|
+
fname = input("Export filename (default .svg if no extension) or number to overwrite (q=cancel): ").strip()
|
|
1684
1724
|
if not fname or fname.lower() == 'q':
|
|
1685
1725
|
print_menu(); continue
|
|
1686
1726
|
|
|
1727
|
+
already_confirmed = False # Initialize for new filename case
|
|
1728
|
+
# Check for 'o' option
|
|
1729
|
+
if fname.lower() == 'o':
|
|
1730
|
+
if not last_figure_path:
|
|
1731
|
+
print("No previous export found.")
|
|
1732
|
+
print_menu(); continue
|
|
1733
|
+
if not os.path.exists(last_figure_path):
|
|
1734
|
+
print(f"Previous export file not found: {last_figure_path}")
|
|
1735
|
+
print_menu(); continue
|
|
1736
|
+
yn = input(f"Overwrite '{os.path.basename(last_figure_path)}'? (y/n): ").strip().lower()
|
|
1737
|
+
if yn != 'y':
|
|
1738
|
+
print_menu(); continue
|
|
1739
|
+
target = last_figure_path
|
|
1740
|
+
already_confirmed = True
|
|
1687
1741
|
# Check if user selected a number
|
|
1688
|
-
|
|
1689
|
-
|
|
1742
|
+
elif fname.isdigit() and files:
|
|
1743
|
+
already_confirmed = False
|
|
1690
1744
|
idx = int(fname)
|
|
1691
1745
|
if 1 <= idx <= len(files):
|
|
1692
1746
|
name = files[idx-1]
|
|
@@ -1746,6 +1800,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1746
1800
|
else:
|
|
1747
1801
|
fig.savefig(target, dpi=300)
|
|
1748
1802
|
print(f"Exported figure to {target}")
|
|
1803
|
+
fig._last_figure_export_path = target
|
|
1749
1804
|
except Exception as e:
|
|
1750
1805
|
print(f"Export failed: {e}")
|
|
1751
1806
|
print_menu(); continue
|
|
@@ -1949,10 +2004,28 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1949
2004
|
print(f" {i}: {f} ({timestamp})")
|
|
1950
2005
|
else:
|
|
1951
2006
|
print(f" {i}: {f}")
|
|
1952
|
-
|
|
2007
|
+
last_session_path = getattr(fig, '_last_session_save_path', None)
|
|
2008
|
+
if last_session_path:
|
|
2009
|
+
prompt = "Enter new filename (no ext needed), number to overwrite, or o to overwrite last (q=cancel): "
|
|
2010
|
+
else:
|
|
2011
|
+
prompt = "Enter new filename (no ext needed) or number to overwrite (q=cancel): "
|
|
1953
2012
|
choice = input(prompt).strip()
|
|
1954
2013
|
if not choice or choice.lower() == 'q':
|
|
1955
2014
|
print_menu(); continue
|
|
2015
|
+
if choice.lower() == 'o':
|
|
2016
|
+
# Overwrite last saved session
|
|
2017
|
+
if not last_session_path:
|
|
2018
|
+
print("No previous save found.")
|
|
2019
|
+
print_menu(); continue
|
|
2020
|
+
if not os.path.exists(last_session_path):
|
|
2021
|
+
print(f"Previous save file not found: {last_session_path}")
|
|
2022
|
+
print_menu(); continue
|
|
2023
|
+
yn = input(f"Overwrite '{os.path.basename(last_session_path)}'? (y/n): ").strip().lower()
|
|
2024
|
+
if yn != 'y':
|
|
2025
|
+
print_menu(); continue
|
|
2026
|
+
dump_operando_session(last_session_path, fig=fig, ax=ax, im=im, cbar=cbar, ec_ax=ec_ax, skip_confirm=True)
|
|
2027
|
+
print(f"Overwritten session to {last_session_path}")
|
|
2028
|
+
print_menu(); continue
|
|
1956
2029
|
if choice.isdigit() and files:
|
|
1957
2030
|
idx = int(choice)
|
|
1958
2031
|
if 1 <= idx <= len(files):
|
|
@@ -1961,10 +2034,13 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1961
2034
|
if yn != 'y':
|
|
1962
2035
|
print_menu(); continue
|
|
1963
2036
|
target = os.path.join(folder, name)
|
|
2037
|
+
dump_operando_session(target, fig=fig, ax=ax, im=im, cbar=cbar, ec_ax=ec_ax, skip_confirm=True)
|
|
2038
|
+
fig._last_session_save_path = target
|
|
2039
|
+
print_menu(); continue
|
|
1964
2040
|
else:
|
|
1965
2041
|
print("Invalid number.")
|
|
1966
2042
|
print_menu(); continue
|
|
1967
|
-
|
|
2043
|
+
if choice.lower() != 'o':
|
|
1968
2044
|
name = choice
|
|
1969
2045
|
root, ext = os.path.splitext(name)
|
|
1970
2046
|
if ext == '':
|
|
@@ -1974,7 +2050,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1974
2050
|
yn = input(f"'{os.path.basename(target)}' exists. Overwrite? (y/n): ").strip().lower()
|
|
1975
2051
|
if yn != 'y':
|
|
1976
2052
|
print_menu(); continue
|
|
1977
|
-
|
|
2053
|
+
dump_operando_session(target, fig=fig, ax=ax, im=im, cbar=cbar, ec_ax=ec_ax, skip_confirm=True)
|
|
2054
|
+
fig._last_session_save_path = target
|
|
1978
2055
|
except Exception as e:
|
|
1979
2056
|
print(f"Save failed: {e}")
|
|
1980
2057
|
print_menu(); continue
|
|
@@ -2840,7 +2917,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2840
2917
|
continue
|
|
2841
2918
|
_snapshot("operando-xrange")
|
|
2842
2919
|
ax.set_xlim(cur[0], new_upper)
|
|
2843
|
-
_renormalize_to_visible()
|
|
2844
2920
|
fig.canvas.draw_idle()
|
|
2845
2921
|
print(f"Operando X range updated: {ax.get_xlim()[0]:.4g} {ax.get_xlim()[1]:.4g}")
|
|
2846
2922
|
if line.lower() == 'w':
|
|
@@ -2860,7 +2936,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2860
2936
|
continue
|
|
2861
2937
|
_snapshot("operando-xrange")
|
|
2862
2938
|
ax.set_xlim(new_lower, cur[1])
|
|
2863
|
-
_renormalize_to_visible()
|
|
2864
2939
|
fig.canvas.draw_idle()
|
|
2865
2940
|
print(f"Operando X range updated: {ax.get_xlim()[0]:.4g} {ax.get_xlim()[1]:.4g}")
|
|
2866
2941
|
if line.lower() == 's':
|
|
@@ -2877,7 +2952,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2877
2952
|
orig_min = min(extent[0], extent[1])
|
|
2878
2953
|
orig_max = max(extent[0], extent[1])
|
|
2879
2954
|
ax.set_xlim(orig_min, orig_max)
|
|
2880
|
-
_renormalize_to_visible()
|
|
2881
2955
|
fig.canvas.draw_idle()
|
|
2882
2956
|
print(f"Operando X range restored to original: {ax.get_xlim()[0]:.4g} {ax.get_xlim()[1]:.4g}")
|
|
2883
2957
|
else:
|
|
@@ -2891,8 +2965,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2891
2965
|
try:
|
|
2892
2966
|
lo, hi = map(float, line.split())
|
|
2893
2967
|
ax.set_xlim(lo, hi)
|
|
2894
|
-
# Re-normalize intensity to visible region
|
|
2895
|
-
_renormalize_to_visible()
|
|
2896
2968
|
fig.canvas.draw_idle()
|
|
2897
2969
|
except Exception as e:
|
|
2898
2970
|
print(f"Invalid range: {e}")
|
|
@@ -2918,7 +2990,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2918
2990
|
continue
|
|
2919
2991
|
_snapshot("operando-yrange")
|
|
2920
2992
|
ax.set_ylim(cur[0], new_upper)
|
|
2921
|
-
_renormalize_to_visible()
|
|
2922
2993
|
fig.canvas.draw_idle()
|
|
2923
2994
|
print(f"Operando Y range updated: {ax.get_ylim()[0]:.4g} {ax.get_ylim()[1]:.4g}")
|
|
2924
2995
|
if line.lower() == 'w':
|
|
@@ -2938,7 +3009,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2938
3009
|
continue
|
|
2939
3010
|
_snapshot("operando-yrange")
|
|
2940
3011
|
ax.set_ylim(new_lower, cur[1])
|
|
2941
|
-
_renormalize_to_visible()
|
|
2942
3012
|
fig.canvas.draw_idle()
|
|
2943
3013
|
print(f"Operando Y range updated: {ax.get_ylim()[0]:.4g} {ax.get_ylim()[1]:.4g}")
|
|
2944
3014
|
if line.lower() == 's':
|
|
@@ -2955,7 +3025,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2955
3025
|
orig_min = min(extent[2], extent[3])
|
|
2956
3026
|
orig_max = max(extent[2], extent[3])
|
|
2957
3027
|
ax.set_ylim(orig_min, orig_max)
|
|
2958
|
-
_renormalize_to_visible()
|
|
2959
3028
|
fig.canvas.draw_idle()
|
|
2960
3029
|
print(f"Operando Y range restored to original: {ax.get_ylim()[0]:.4g} {ax.get_ylim()[1]:.4g}")
|
|
2961
3030
|
else:
|
|
@@ -2969,8 +3038,6 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2969
3038
|
try:
|
|
2970
3039
|
lo, hi = map(float, line.split())
|
|
2971
3040
|
ax.set_ylim(lo, hi)
|
|
2972
|
-
# Re-normalize intensity to visible region
|
|
2973
|
-
_renormalize_to_visible()
|
|
2974
3041
|
fig.canvas.draw_idle()
|
|
2975
3042
|
except Exception as e:
|
|
2976
3043
|
print(f"Invalid range: {e}")
|
|
@@ -3437,6 +3504,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3437
3504
|
else:
|
|
3438
3505
|
print(f" {_i}: {fname}")
|
|
3439
3506
|
|
|
3507
|
+
last_style_path = getattr(fig, '_last_style_export_path', None)
|
|
3440
3508
|
if ec_ax is None:
|
|
3441
3509
|
print("\nNote: Style export (.bps/.bpsg) is only available in dual-pane mode (with EC file).")
|
|
3442
3510
|
sub = input("Style submenu: (q=return, r=refresh): ").strip().lower()
|
|
@@ -3448,11 +3516,38 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3448
3516
|
print("Unknown choice.")
|
|
3449
3517
|
continue
|
|
3450
3518
|
else:
|
|
3451
|
-
|
|
3519
|
+
if last_style_path:
|
|
3520
|
+
sub = input("Style submenu: (e=export, o=overwrite last, q=return, r=refresh): ").strip().lower()
|
|
3521
|
+
else:
|
|
3522
|
+
sub = input("Style submenu: (e=export, q=return, r=refresh): ").strip().lower()
|
|
3452
3523
|
if sub == 'q':
|
|
3453
3524
|
break
|
|
3454
3525
|
if sub == 'r' or sub == '':
|
|
3455
3526
|
continue
|
|
3527
|
+
if sub == 'o':
|
|
3528
|
+
# Overwrite last exported style file
|
|
3529
|
+
if not last_style_path:
|
|
3530
|
+
print("No previous export found.")
|
|
3531
|
+
continue
|
|
3532
|
+
if not os.path.exists(last_style_path):
|
|
3533
|
+
print(f"Previous export file not found: {last_style_path}")
|
|
3534
|
+
continue
|
|
3535
|
+
yn = input(f"Overwrite '{os.path.basename(last_style_path)}'? (y/n): ").strip().lower()
|
|
3536
|
+
if yn != 'y':
|
|
3537
|
+
continue
|
|
3538
|
+
# Determine export type from existing file and rebuild config
|
|
3539
|
+
try:
|
|
3540
|
+
with open(last_style_path, 'r', encoding='utf-8') as f:
|
|
3541
|
+
old_cfg = json.load(f)
|
|
3542
|
+
old_kind = old_cfg.get('kind', '')
|
|
3543
|
+
# Need to rebuild the full config - this requires the same logic as 'e' command
|
|
3544
|
+
# For simplicity, redirect user to use 'e' for now, or we could duplicate the config building code
|
|
3545
|
+
print("To overwrite with current style, please use 'e' to export fresh.")
|
|
3546
|
+
print("The 'o' option will be enhanced in a future update to rebuild config automatically.")
|
|
3547
|
+
continue
|
|
3548
|
+
except Exception as e:
|
|
3549
|
+
print(f"Error reading previous export: {e}")
|
|
3550
|
+
continue
|
|
3456
3551
|
if sub == 'e':
|
|
3457
3552
|
# Ask for ps or psg
|
|
3458
3553
|
print("Export options:")
|
|
@@ -3572,6 +3667,23 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3572
3667
|
'x': getattr(ec_ax.xaxis, 'labelpad', None),
|
|
3573
3668
|
'y': getattr(ec_ax.yaxis, 'labelpad', None),
|
|
3574
3669
|
}
|
|
3670
|
+
# Capture title offsets
|
|
3671
|
+
op_title_offsets = {
|
|
3672
|
+
'top_y': float(getattr(ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3673
|
+
'top_x': float(getattr(ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3674
|
+
'bottom_y': float(getattr(ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3675
|
+
'left_x': float(getattr(ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3676
|
+
'right_x': float(getattr(ax, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3677
|
+
'right_y': float(getattr(ax, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3678
|
+
}
|
|
3679
|
+
ec_title_offsets = {
|
|
3680
|
+
'top_y': float(getattr(ec_ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3681
|
+
'top_x': float(getattr(ec_ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3682
|
+
'bottom_y': float(getattr(ec_ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3683
|
+
'left_x': float(getattr(ec_ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3684
|
+
'right_x': float(getattr(ec_ax, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
3685
|
+
'right_y': float(getattr(ec_ax, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
3686
|
+
}
|
|
3575
3687
|
|
|
3576
3688
|
if exp_choice == 'ps':
|
|
3577
3689
|
cb_h_offset = getattr(cbar.ax, '_cb_h_offset_in', 0.0)
|
|
@@ -3581,8 +3693,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3581
3693
|
'version': 2,
|
|
3582
3694
|
'figure': {'canvas_size': [fig_w, fig_h], 'cb_visible': cb_visible, 'cb_label_mode': cb_label_mode},
|
|
3583
3695
|
'geometry': {'op_w_in': ax_w_in, 'op_h_in': ax_h_in, 'ec_w_in': ec_w_in, 'cb_h_offset': float(cb_h_offset), 'ec_h_offset': float(ec_h_offset) if ec_h_offset is not None else None},
|
|
3584
|
-
'operando': {'cmap': cmap_name, 'wasd_state': op_wasd_state, 'spines': op_spines, 'ticks': {'widths': op_ticks}, 'y_reversed': op_reversed, 'intensity_range': intensity_range, 'labelpads': op_labelpads},
|
|
3585
|
-
'ec': {'wasd_state': ec_wasd_state, 'spines': ec_spines, 'ticks': {'widths': ec_ticks}, 'curve': ec_curve, 'y_reversed': ec_reversed, 'y_mode': ec_y_mode, 'ion_params': ion_params, 'visible': ec_visible, 'labelpads': ec_labelpads},
|
|
3696
|
+
'operando': {'cmap': cmap_name, 'wasd_state': op_wasd_state, 'spines': op_spines, 'ticks': {'widths': op_ticks}, 'y_reversed': op_reversed, 'intensity_range': intensity_range, 'labelpads': op_labelpads, 'title_offsets': op_title_offsets},
|
|
3697
|
+
'ec': {'wasd_state': ec_wasd_state, 'spines': ec_spines, 'ticks': {'widths': ec_ticks}, 'curve': ec_curve, 'y_reversed': ec_reversed, 'y_mode': ec_y_mode, 'ion_params': ion_params, 'visible': ec_visible, 'labelpads': ec_labelpads, 'title_offsets': ec_title_offsets},
|
|
3586
3698
|
'font': {'family': fam, 'size': fsize},
|
|
3587
3699
|
'colorbar': {'label': cb_label_text, 'mode': cb_label_mode, 'visible': cb_visible},
|
|
3588
3700
|
}
|
|
@@ -3595,8 +3707,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3595
3707
|
'version': 2,
|
|
3596
3708
|
'figure': {'canvas_size': [fig_w, fig_h], 'cb_visible': cb_visible, 'cb_label_mode': cb_label_mode},
|
|
3597
3709
|
'geometry': {'op_w_in': ax_w_in, 'op_h_in': ax_h_in, 'ec_w_in': ec_w_in, 'cb_h_offset': float(cb_h_offset), 'ec_h_offset': float(ec_h_offset) if ec_h_offset is not None else None},
|
|
3598
|
-
'operando': {'cmap': cmap_name, 'wasd_state': op_wasd_state, 'spines': op_spines, 'ticks': {'widths': op_ticks}, 'y_reversed': op_reversed, 'intensity_range': intensity_range, 'labelpads': op_labelpads},
|
|
3599
|
-
'ec': {'wasd_state': ec_wasd_state, 'spines': ec_spines, 'ticks': {'widths': ec_ticks}, 'curve': ec_curve, 'y_reversed': ec_reversed, 'y_mode': ec_y_mode, 'ion_params': ion_params, 'visible': ec_visible, 'labelpads': ec_labelpads},
|
|
3710
|
+
'operando': {'cmap': cmap_name, 'wasd_state': op_wasd_state, 'spines': op_spines, 'ticks': {'widths': op_ticks}, 'y_reversed': op_reversed, 'intensity_range': intensity_range, 'labelpads': op_labelpads, 'title_offsets': op_title_offsets},
|
|
3711
|
+
'ec': {'wasd_state': ec_wasd_state, 'spines': ec_spines, 'ticks': {'widths': ec_ticks}, 'curve': ec_curve, 'y_reversed': ec_reversed, 'y_mode': ec_y_mode, 'ion_params': ion_params, 'visible': ec_visible, 'labelpads': ec_labelpads, 'title_offsets': ec_title_offsets},
|
|
3600
3712
|
'font': {'family': fam, 'size': fsize},
|
|
3601
3713
|
'axes_geometry': _get_geometry_snapshot(ax, ec_ax),
|
|
3602
3714
|
'colorbar': {'label': cb_label_text, 'mode': cb_label_mode, 'visible': cb_visible},
|
|
@@ -4197,6 +4309,33 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
4197
4309
|
except Exception:
|
|
4198
4310
|
pass
|
|
4199
4311
|
|
|
4312
|
+
# Restore title offsets BEFORE applying labelpads
|
|
4313
|
+
if version >= 2:
|
|
4314
|
+
try:
|
|
4315
|
+
op_offsets = op.get('title_offsets', {})
|
|
4316
|
+
if op_offsets:
|
|
4317
|
+
ax._top_xlabel_manual_offset_y_pts = float(op_offsets.get('top_y', 0.0) or 0.0)
|
|
4318
|
+
ax._top_xlabel_manual_offset_x_pts = float(op_offsets.get('top_x', 0.0) or 0.0)
|
|
4319
|
+
ax._bottom_xlabel_manual_offset_y_pts = float(op_offsets.get('bottom_y', 0.0) or 0.0)
|
|
4320
|
+
ax._left_ylabel_manual_offset_x_pts = float(op_offsets.get('left_x', 0.0) or 0.0)
|
|
4321
|
+
ax._right_ylabel_manual_offset_x_pts = float(op_offsets.get('right_x', 0.0) or 0.0)
|
|
4322
|
+
ax._right_ylabel_manual_offset_y_pts = float(op_offsets.get('right_y', 0.0) or 0.0)
|
|
4323
|
+
except Exception as e:
|
|
4324
|
+
print(f"Warning: Could not apply operando title offsets: {e}")
|
|
4325
|
+
|
|
4326
|
+
try:
|
|
4327
|
+
ec_cfg = cfg.get('ec', {})
|
|
4328
|
+
ec_offsets = ec_cfg.get('title_offsets', {})
|
|
4329
|
+
if ec_offsets and ec_ax is not None:
|
|
4330
|
+
ec_ax._top_xlabel_manual_offset_y_pts = float(ec_offsets.get('top_y', 0.0) or 0.0)
|
|
4331
|
+
ec_ax._top_xlabel_manual_offset_x_pts = float(ec_offsets.get('top_x', 0.0) or 0.0)
|
|
4332
|
+
ec_ax._bottom_xlabel_manual_offset_y_pts = float(ec_offsets.get('bottom_y', 0.0) or 0.0)
|
|
4333
|
+
ec_ax._left_ylabel_manual_offset_x_pts = float(ec_offsets.get('left_x', 0.0) or 0.0)
|
|
4334
|
+
ec_ax._right_ylabel_manual_offset_x_pts = float(ec_offsets.get('right_x', 0.0) or 0.0)
|
|
4335
|
+
ec_ax._right_ylabel_manual_offset_y_pts = float(ec_offsets.get('right_y', 0.0) or 0.0)
|
|
4336
|
+
except Exception as e:
|
|
4337
|
+
print(f"Warning: Could not apply EC title offsets: {e}")
|
|
4338
|
+
|
|
4200
4339
|
# Apply labelpads (title positioning) - preserve current if not in config
|
|
4201
4340
|
if version >= 2:
|
|
4202
4341
|
try:
|
|
@@ -4240,6 +4379,44 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
4240
4379
|
except Exception as e:
|
|
4241
4380
|
print(f"Warning: Could not apply EC labelpads: {e}")
|
|
4242
4381
|
|
|
4382
|
+
# Reposition titles to apply offsets (after labelpads are set)
|
|
4383
|
+
try:
|
|
4384
|
+
from .ui import position_top_xlabel as _ui_position_top_xlabel, position_bottom_xlabel as _ui_position_bottom_xlabel, position_left_ylabel as _ui_position_left_ylabel, position_right_ylabel as _ui_position_right_ylabel
|
|
4385
|
+
# Build tick_state for operando pane
|
|
4386
|
+
op_ts = getattr(ax, '_saved_tick_state', {})
|
|
4387
|
+
op_tick_state = {
|
|
4388
|
+
't_ticks': bool(op_ts.get('t_ticks', op_ts.get('tx', False))),
|
|
4389
|
+
't_labels': bool(op_ts.get('t_labels', op_ts.get('tx', False))),
|
|
4390
|
+
'b_ticks': bool(op_ts.get('b_ticks', op_ts.get('bx', True))),
|
|
4391
|
+
'b_labels': bool(op_ts.get('b_labels', op_ts.get('bx', True))),
|
|
4392
|
+
'l_ticks': bool(op_ts.get('l_ticks', op_ts.get('ly', True))),
|
|
4393
|
+
'l_labels': bool(op_ts.get('l_labels', op_ts.get('ly', True))),
|
|
4394
|
+
'r_ticks': bool(op_ts.get('r_ticks', op_ts.get('ry', False))),
|
|
4395
|
+
'r_labels': bool(op_ts.get('r_labels', op_ts.get('ry', False))),
|
|
4396
|
+
}
|
|
4397
|
+
_ui_position_top_xlabel(ax, fig, op_tick_state)
|
|
4398
|
+
_ui_position_bottom_xlabel(ax, fig, op_tick_state)
|
|
4399
|
+
_ui_position_left_ylabel(ax, fig, op_tick_state)
|
|
4400
|
+
_ui_position_right_ylabel(ax, fig, op_tick_state)
|
|
4401
|
+
if ec_ax is not None:
|
|
4402
|
+
ec_ts = getattr(ec_ax, '_saved_tick_state', {})
|
|
4403
|
+
ec_tick_state = {
|
|
4404
|
+
't_ticks': bool(ec_ts.get('t_ticks', ec_ts.get('tx', False))),
|
|
4405
|
+
't_labels': bool(ec_ts.get('t_labels', ec_ts.get('tx', False))),
|
|
4406
|
+
'b_ticks': bool(ec_ts.get('b_ticks', ec_ts.get('bx', True))),
|
|
4407
|
+
'b_labels': bool(ec_ts.get('b_labels', ec_ts.get('bx', True))),
|
|
4408
|
+
'l_ticks': bool(ec_ts.get('l_ticks', ec_ts.get('ly', True))),
|
|
4409
|
+
'l_labels': bool(ec_ts.get('l_labels', ec_ts.get('ly', True))),
|
|
4410
|
+
'r_ticks': bool(ec_ts.get('r_ticks', ec_ts.get('ry', False))),
|
|
4411
|
+
'r_labels': bool(ec_ts.get('r_labels', ec_ts.get('ry', False))),
|
|
4412
|
+
}
|
|
4413
|
+
_ui_position_top_xlabel(ec_ax, fig, ec_tick_state)
|
|
4414
|
+
_ui_position_bottom_xlabel(ec_ax, fig, ec_tick_state)
|
|
4415
|
+
_ui_position_left_ylabel(ec_ax, fig, ec_tick_state)
|
|
4416
|
+
_ui_position_right_ylabel(ec_ax, fig, ec_tick_state)
|
|
4417
|
+
except Exception as e:
|
|
4418
|
+
print(f"Warning: Could not reposition titles: {e}")
|
|
4419
|
+
|
|
4243
4420
|
# Final redraw
|
|
4244
4421
|
try:
|
|
4245
4422
|
fig.canvas.draw()
|
batplot/session.py
CHANGED
|
@@ -38,6 +38,7 @@ import matplotlib.pyplot as plt
|
|
|
38
38
|
import numpy as np
|
|
39
39
|
|
|
40
40
|
from .utils import _confirm_overwrite
|
|
41
|
+
from .color_utils import ensure_colormap
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
def _current_tick_width(axis_obj, which: str):
|
|
@@ -700,6 +701,16 @@ def dump_operando_session(
|
|
|
700
701
|
ec_wasd_state = _capture_wasd_state(ec_ax)
|
|
701
702
|
ec_spines, ec_ticks = _capture_spine_tick_widths(ec_ax)
|
|
702
703
|
|
|
704
|
+
# Capture EC title offsets
|
|
705
|
+
ec_title_offsets = {
|
|
706
|
+
'top_y': float(getattr(ec_ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
707
|
+
'top_x': float(getattr(ec_ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
708
|
+
'bottom_y': float(getattr(ec_ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
709
|
+
'left_x': float(getattr(ec_ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
710
|
+
'right_x': float(getattr(ec_ax, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
711
|
+
'right_y': float(getattr(ec_ax, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
712
|
+
}
|
|
713
|
+
|
|
703
714
|
ec_state = {
|
|
704
715
|
'time_h': time_h,
|
|
705
716
|
'volt_v': volt_v,
|
|
@@ -715,6 +726,7 @@ def dump_operando_session(
|
|
|
715
726
|
'wasd_state': ec_wasd_state,
|
|
716
727
|
'spines': ec_spines,
|
|
717
728
|
'ticks': {'widths': ec_ticks},
|
|
729
|
+
'title_offsets': ec_title_offsets,
|
|
718
730
|
'stored_ylabel': getattr(ec_ax, '_stored_ylabel', None), # Save hidden ylabel text
|
|
719
731
|
'visible': bool(ec_ax.get_visible()),
|
|
720
732
|
}
|
|
@@ -749,6 +761,7 @@ def dump_operando_session(
|
|
|
749
761
|
'wasd_state': op_wasd_state,
|
|
750
762
|
'spines': op_spines,
|
|
751
763
|
'ticks': {'widths': op_ticks},
|
|
764
|
+
'title_offsets': op_title_offsets,
|
|
752
765
|
'stored_ylabel': getattr(ax, '_stored_ylabel', None), # Save hidden ylabel text
|
|
753
766
|
},
|
|
754
767
|
'colorbar': {
|
|
@@ -831,8 +844,14 @@ def load_operando_session(filename: str):
|
|
|
831
844
|
op = sess['operando']
|
|
832
845
|
arr = _ma.masked_invalid(op['array'])
|
|
833
846
|
extent = tuple(op['extent']) if op['extent'] is not None else None
|
|
847
|
+
cmap_name = op.get('cmap') or 'viridis'
|
|
848
|
+
try:
|
|
849
|
+
if not ensure_colormap(cmap_name):
|
|
850
|
+
cmap_name = 'viridis'
|
|
851
|
+
except Exception:
|
|
852
|
+
cmap_name = 'viridis'
|
|
834
853
|
im = ax.imshow(arr, aspect='auto', origin=op.get('origin', 'upper'), extent=extent,
|
|
835
|
-
cmap=
|
|
854
|
+
cmap=cmap_name, interpolation=op.get('interpolation', 'nearest'))
|
|
836
855
|
if op.get('clim'):
|
|
837
856
|
try:
|
|
838
857
|
im.set_clim(*op['clim'])
|
|
@@ -932,6 +951,19 @@ def load_operando_session(filename: str):
|
|
|
932
951
|
stored_ylabel = op.get('stored_ylabel')
|
|
933
952
|
if stored_ylabel is not None:
|
|
934
953
|
setattr(ax, '_stored_ylabel', stored_ylabel)
|
|
954
|
+
|
|
955
|
+
# Restore operando title offsets
|
|
956
|
+
try:
|
|
957
|
+
op_title_offsets = op.get('title_offsets', {})
|
|
958
|
+
if op_title_offsets:
|
|
959
|
+
ax._top_xlabel_manual_offset_y_pts = float(op_title_offsets.get('top_y', 0.0) or 0.0)
|
|
960
|
+
ax._top_xlabel_manual_offset_x_pts = float(op_title_offsets.get('top_x', 0.0) or 0.0)
|
|
961
|
+
ax._bottom_xlabel_manual_offset_y_pts = float(op_title_offsets.get('bottom_y', 0.0) or 0.0)
|
|
962
|
+
ax._left_ylabel_manual_offset_x_pts = float(op_title_offsets.get('left_x', 0.0) or 0.0)
|
|
963
|
+
ax._right_ylabel_manual_offset_x_pts = float(op_title_offsets.get('right_x', 0.0) or 0.0)
|
|
964
|
+
ax._right_ylabel_manual_offset_y_pts = float(op_title_offsets.get('right_y', 0.0) or 0.0)
|
|
965
|
+
except Exception:
|
|
966
|
+
pass
|
|
935
967
|
|
|
936
968
|
# Apply operando spines
|
|
937
969
|
op_spines = op.get('spines', {})
|
|
@@ -1182,6 +1214,19 @@ def load_operando_session(filename: str):
|
|
|
1182
1214
|
if stored_ylabel is not None:
|
|
1183
1215
|
setattr(ec_ax, '_stored_ylabel', stored_ylabel)
|
|
1184
1216
|
|
|
1217
|
+
# Restore EC title offsets
|
|
1218
|
+
try:
|
|
1219
|
+
ec_title_offsets = ec.get('title_offsets', {})
|
|
1220
|
+
if ec_title_offsets:
|
|
1221
|
+
ec_ax._top_xlabel_manual_offset_y_pts = float(ec_title_offsets.get('top_y', 0.0) or 0.0)
|
|
1222
|
+
ec_ax._top_xlabel_manual_offset_x_pts = float(ec_title_offsets.get('top_x', 0.0) or 0.0)
|
|
1223
|
+
ec_ax._bottom_xlabel_manual_offset_y_pts = float(ec_title_offsets.get('bottom_y', 0.0) or 0.0)
|
|
1224
|
+
ec_ax._left_ylabel_manual_offset_x_pts = float(ec_title_offsets.get('left_x', 0.0) or 0.0)
|
|
1225
|
+
ec_ax._right_ylabel_manual_offset_x_pts = float(ec_title_offsets.get('right_x', 0.0) or 0.0)
|
|
1226
|
+
ec_ax._right_ylabel_manual_offset_y_pts = float(ec_title_offsets.get('right_y', 0.0) or 0.0)
|
|
1227
|
+
except Exception:
|
|
1228
|
+
pass
|
|
1229
|
+
|
|
1185
1230
|
# Apply EC spines (WASD state already applied above)
|
|
1186
1231
|
if version >= 2:
|
|
1187
1232
|
# Apply EC spines
|
|
@@ -1420,6 +1465,15 @@ def dump_ec_session(
|
|
|
1420
1465
|
'top_x': bool(getattr(ax, '_top_xlabel_on', False)),
|
|
1421
1466
|
'right_y': bool(getattr(ax, '_right_ylabel_on', False)),
|
|
1422
1467
|
}
|
|
1468
|
+
# Title offsets
|
|
1469
|
+
title_offsets = {
|
|
1470
|
+
'top_y': float(getattr(ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
1471
|
+
'top_x': float(getattr(ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
1472
|
+
'bottom_y': float(getattr(ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
1473
|
+
'left_x': float(getattr(ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
1474
|
+
'right_x': float(getattr(ax, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
1475
|
+
'right_y': float(getattr(ax, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
1476
|
+
}
|
|
1423
1477
|
# Subplot margins
|
|
1424
1478
|
sp = fig.subplotpars
|
|
1425
1479
|
subplot_margins = {
|
|
@@ -1538,6 +1592,7 @@ def dump_ec_session(
|
|
|
1538
1592
|
'tick_direction': tick_direction,
|
|
1539
1593
|
'spines': spines_state,
|
|
1540
1594
|
'titles': titles,
|
|
1595
|
+
'title_offsets': title_offsets,
|
|
1541
1596
|
'mode': getattr(ax, '_is_dqdv_mode', None), # Store dQdV mode flag
|
|
1542
1597
|
'rotation_angle': getattr(fig, '_ec_rotation_angle', 0), # Store rotation angle
|
|
1543
1598
|
'source_paths': list(getattr(fig, '_bp_source_paths', []) or []),
|
|
@@ -1975,6 +2030,19 @@ def load_ec_session(filename: str):
|
|
|
1975
2030
|
ax.set_ylabel('')
|
|
1976
2031
|
ax.yaxis.label.set_visible(False)
|
|
1977
2032
|
|
|
2033
|
+
# Restore title offsets BEFORE positioning titles
|
|
2034
|
+
try:
|
|
2035
|
+
title_offsets = sess.get('title_offsets', {})
|
|
2036
|
+
if title_offsets:
|
|
2037
|
+
ax._top_xlabel_manual_offset_y_pts = float(title_offsets.get('top_y', 0.0) or 0.0)
|
|
2038
|
+
ax._top_xlabel_manual_offset_x_pts = float(title_offsets.get('top_x', 0.0) or 0.0)
|
|
2039
|
+
ax._bottom_xlabel_manual_offset_y_pts = float(title_offsets.get('bottom_y', 0.0) or 0.0)
|
|
2040
|
+
ax._left_ylabel_manual_offset_x_pts = float(title_offsets.get('left_x', 0.0) or 0.0)
|
|
2041
|
+
ax._right_ylabel_manual_offset_x_pts = float(title_offsets.get('right_x', 0.0) or 0.0)
|
|
2042
|
+
ax._right_ylabel_manual_offset_y_pts = float(title_offsets.get('right_y', 0.0) or 0.0)
|
|
2043
|
+
except Exception:
|
|
2044
|
+
pass
|
|
2045
|
+
|
|
1978
2046
|
# Duplicate titles
|
|
1979
2047
|
try:
|
|
1980
2048
|
titles = sess.get('titles', {})
|
|
@@ -2255,6 +2323,15 @@ def dump_cpc_session(
|
|
|
2255
2323
|
'top_xlabel': getattr(ax, '_stored_top_xlabel', ''),
|
|
2256
2324
|
'right_ylabel': getattr(ax2, '_stored_ylabel', ax2.get_ylabel()),
|
|
2257
2325
|
}
|
|
2326
|
+
# Title offsets
|
|
2327
|
+
title_offsets = {
|
|
2328
|
+
'top_y': float(getattr(ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
2329
|
+
'top_x': float(getattr(ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
2330
|
+
'bottom_y': float(getattr(ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
2331
|
+
'left_x': float(getattr(ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
2332
|
+
'right_x': float(getattr(ax2, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
|
|
2333
|
+
'right_y': float(getattr(ax2, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
|
|
2334
|
+
}
|
|
2258
2335
|
|
|
2259
2336
|
meta = {
|
|
2260
2337
|
'kind': 'cpc',
|
|
@@ -2317,6 +2394,7 @@ def dump_cpc_session(
|
|
|
2317
2394
|
'wasd_state': wasd_state,
|
|
2318
2395
|
'tick_widths': tick_widths,
|
|
2319
2396
|
'stored_titles': stored_titles,
|
|
2397
|
+
'title_offsets': title_offsets,
|
|
2320
2398
|
'font': {
|
|
2321
2399
|
'size': plt.rcParams.get('font.size'),
|
|
2322
2400
|
'chain': list(plt.rcParams.get('font.sans-serif', [])),
|
|
@@ -2653,6 +2731,19 @@ def load_cpc_session(filename: str):
|
|
|
2653
2731
|
except Exception:
|
|
2654
2732
|
pass
|
|
2655
2733
|
|
|
2734
|
+
# Restore title offsets BEFORE restoring titles
|
|
2735
|
+
try:
|
|
2736
|
+
title_offsets = sess.get('title_offsets', {})
|
|
2737
|
+
if title_offsets:
|
|
2738
|
+
ax._top_xlabel_manual_offset_y_pts = float(title_offsets.get('top_y', 0.0) or 0.0)
|
|
2739
|
+
ax._top_xlabel_manual_offset_x_pts = float(title_offsets.get('top_x', 0.0) or 0.0)
|
|
2740
|
+
ax._bottom_xlabel_manual_offset_y_pts = float(title_offsets.get('bottom_y', 0.0) or 0.0)
|
|
2741
|
+
ax._left_ylabel_manual_offset_x_pts = float(title_offsets.get('left_x', 0.0) or 0.0)
|
|
2742
|
+
ax2._right_ylabel_manual_offset_x_pts = float(title_offsets.get('right_x', 0.0) or 0.0)
|
|
2743
|
+
ax2._right_ylabel_manual_offset_y_pts = float(title_offsets.get('right_y', 0.0) or 0.0)
|
|
2744
|
+
except Exception:
|
|
2745
|
+
pass
|
|
2746
|
+
|
|
2656
2747
|
# Restore stored title texts (version 2+)
|
|
2657
2748
|
try:
|
|
2658
2749
|
stored_titles = sess.get('stored_titles', {})
|