batplot 1.7.23__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/cpc_interactive.py +168 -11
- batplot/electrochem_interactive.py +152 -14
- batplot/interactive.py +156 -30
- batplot/operando_ec_interactive.py +162 -11
- batplot/session.py +84 -0
- batplot/style.py +109 -47
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/METADATA +23 -2
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/RECORD +13 -13
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/WHEEL +0 -0
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/entry_points.txt +0 -0
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.7.23.dist-info → batplot-1.7.24.dist-info}/top_level.txt +0 -0
batplot/interactive.py
CHANGED
|
@@ -866,10 +866,11 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
866
866
|
)
|
|
867
867
|
|
|
868
868
|
# NEW: export current style to .bpcfg
|
|
869
|
-
def export_style_config(filename, base_path=None):
|
|
869
|
+
def export_style_config(filename, base_path=None, overwrite_path=None):
|
|
870
870
|
cts = getattr(_bp, 'cif_tick_series', None) if _bp is not None else None
|
|
871
871
|
show_titles = bool(getattr(_bp, 'show_cif_titles', True)) if _bp is not None else True
|
|
872
|
-
|
|
872
|
+
from .style import export_style_config as _export_style_config
|
|
873
|
+
return _export_style_config(
|
|
873
874
|
filename,
|
|
874
875
|
fig,
|
|
875
876
|
ax,
|
|
@@ -883,6 +884,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
883
884
|
label_text_objects,
|
|
884
885
|
base_path,
|
|
885
886
|
show_cif_titles=show_titles,
|
|
887
|
+
overwrite_path=overwrite_path,
|
|
886
888
|
)
|
|
887
889
|
|
|
888
890
|
# NEW: apply imported style config (restricted application)
|
|
@@ -1467,6 +1469,17 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1467
1469
|
else:
|
|
1468
1470
|
continue
|
|
1469
1471
|
elif key == 'z': # toggle hkl labels on CIF ticks (non-blocking)
|
|
1472
|
+
# Check if CIF files exist before allowing this command
|
|
1473
|
+
has_cif = False
|
|
1474
|
+
try:
|
|
1475
|
+
has_cif = any(f.split(':')[0].lower().endswith('.cif') for f in args.files)
|
|
1476
|
+
if not has_cif and _bp is not None:
|
|
1477
|
+
has_cif = bool(getattr(_bp, 'cif_tick_series', None))
|
|
1478
|
+
except Exception:
|
|
1479
|
+
pass
|
|
1480
|
+
if not has_cif:
|
|
1481
|
+
print("Unknown option.")
|
|
1482
|
+
continue
|
|
1470
1483
|
try:
|
|
1471
1484
|
# Flip visibility flag in batplot module
|
|
1472
1485
|
cur = bool(getattr(_bp, 'show_cif_hkl', False)) if _bp is not None else False
|
|
@@ -1546,6 +1559,17 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1546
1559
|
print(f"Error in legend submenu: {e}")
|
|
1547
1560
|
continue
|
|
1548
1561
|
elif key == 'j': # toggle CIF title labels (filename labels)
|
|
1562
|
+
# Check if CIF files exist before allowing this command
|
|
1563
|
+
has_cif = False
|
|
1564
|
+
try:
|
|
1565
|
+
has_cif = any(f.split(':')[0].lower().endswith('.cif') for f in args.files)
|
|
1566
|
+
if not has_cif and _bp is not None:
|
|
1567
|
+
has_cif = bool(getattr(_bp, 'cif_tick_series', None))
|
|
1568
|
+
except Exception:
|
|
1569
|
+
pass
|
|
1570
|
+
if not has_cif:
|
|
1571
|
+
print("Unknown option.")
|
|
1572
|
+
continue
|
|
1549
1573
|
try:
|
|
1550
1574
|
# Preserve both x and y-axis limits to prevent movement
|
|
1551
1575
|
prev_xlim = ax.get_xlim()
|
|
@@ -1609,11 +1633,47 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1609
1633
|
print(f" {i}: {f} ({timestamp})")
|
|
1610
1634
|
else:
|
|
1611
1635
|
print(f" {i}: {f}")
|
|
1612
|
-
|
|
1636
|
+
last_session_path = getattr(fig, '_last_session_save_path', None)
|
|
1637
|
+
if last_session_path:
|
|
1638
|
+
prompt = "Enter new filename (no ext needed), number to overwrite, or o to overwrite last (q=cancel): "
|
|
1639
|
+
else:
|
|
1640
|
+
prompt = "Enter new filename (no ext needed) or number to overwrite (q=cancel): "
|
|
1613
1641
|
choice = input(prompt).strip()
|
|
1614
1642
|
if not choice or choice.lower() == 'q':
|
|
1615
1643
|
print("Canceled.")
|
|
1616
1644
|
continue
|
|
1645
|
+
if choice.lower() == 'o':
|
|
1646
|
+
# Overwrite last saved session
|
|
1647
|
+
if not last_session_path:
|
|
1648
|
+
print("No previous save found.")
|
|
1649
|
+
continue
|
|
1650
|
+
if not os.path.exists(last_session_path):
|
|
1651
|
+
print(f"Previous save file not found: {last_session_path}")
|
|
1652
|
+
continue
|
|
1653
|
+
yn = input(f"Overwrite '{os.path.basename(last_session_path)}'? (y/n): ").strip().lower()
|
|
1654
|
+
if yn != 'y':
|
|
1655
|
+
continue
|
|
1656
|
+
_bp_dump_session(
|
|
1657
|
+
last_session_path,
|
|
1658
|
+
fig=fig,
|
|
1659
|
+
ax=ax,
|
|
1660
|
+
x_data_list=x_data_list,
|
|
1661
|
+
y_data_list=y_data_list,
|
|
1662
|
+
orig_y=orig_y,
|
|
1663
|
+
offsets_list=offsets_list,
|
|
1664
|
+
labels=labels,
|
|
1665
|
+
delta=delta,
|
|
1666
|
+
args=args,
|
|
1667
|
+
tick_state=tick_state,
|
|
1668
|
+
cif_tick_series=(getattr(_bp, 'cif_tick_series', None) if _bp is not None else None),
|
|
1669
|
+
cif_hkl_map=(getattr(_bp, 'cif_hkl_map', None) if _bp is not None else None),
|
|
1670
|
+
cif_hkl_label_map=(getattr(_bp, 'cif_hkl_label_map', None) if _bp is not None else None),
|
|
1671
|
+
show_cif_hkl=(bool(getattr(_bp,'show_cif_hkl', False)) if _bp is not None else False),
|
|
1672
|
+
show_cif_titles=(bool(getattr(_bp,'show_cif_titles', True)) if _bp is not None else True),
|
|
1673
|
+
skip_confirm=True,
|
|
1674
|
+
)
|
|
1675
|
+
print(f"Overwritten session to {last_session_path}")
|
|
1676
|
+
continue
|
|
1617
1677
|
target_path = None
|
|
1618
1678
|
# Overwrite by number
|
|
1619
1679
|
if choice.isdigit() and files:
|
|
@@ -1626,10 +1686,32 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1626
1686
|
continue
|
|
1627
1687
|
target_path = os.path.join(folder, name)
|
|
1628
1688
|
skip_confirm = True # Already confirmed above
|
|
1689
|
+
_bp_dump_session(
|
|
1690
|
+
target_path,
|
|
1691
|
+
fig=fig,
|
|
1692
|
+
ax=ax,
|
|
1693
|
+
x_data_list=x_data_list,
|
|
1694
|
+
y_data_list=y_data_list,
|
|
1695
|
+
orig_y=orig_y,
|
|
1696
|
+
offsets_list=offsets_list,
|
|
1697
|
+
labels=labels,
|
|
1698
|
+
delta=delta,
|
|
1699
|
+
args=args,
|
|
1700
|
+
tick_state=tick_state,
|
|
1701
|
+
cif_tick_series=(getattr(_bp, 'cif_tick_series', None) if _bp is not None else None),
|
|
1702
|
+
cif_hkl_map=(getattr(_bp, 'cif_hkl_map', None) if _bp is not None else None),
|
|
1703
|
+
cif_hkl_label_map=(getattr(_bp, 'cif_hkl_label_map', None) if _bp is not None else None),
|
|
1704
|
+
show_cif_hkl=(bool(getattr(_bp,'show_cif_hkl', False)) if _bp is not None else False),
|
|
1705
|
+
show_cif_titles=(bool(getattr(_bp,'show_cif_titles', True)) if _bp is not None else True),
|
|
1706
|
+
skip_confirm=skip_confirm,
|
|
1707
|
+
)
|
|
1708
|
+
print(f"Saved session to {target_path}")
|
|
1709
|
+
fig._last_session_save_path = target_path
|
|
1710
|
+
continue
|
|
1629
1711
|
else:
|
|
1630
1712
|
print("Invalid number.")
|
|
1631
1713
|
continue
|
|
1632
|
-
|
|
1714
|
+
if choice.lower() != 'o':
|
|
1633
1715
|
# New name, allow relative or absolute
|
|
1634
1716
|
name = choice
|
|
1635
1717
|
root, ext = os.path.splitext(name)
|
|
@@ -1643,27 +1725,28 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1643
1725
|
print("Canceled.")
|
|
1644
1726
|
continue
|
|
1645
1727
|
skip_confirm = True # Already confirmed
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1728
|
+
# Delegate to session dumper
|
|
1729
|
+
_bp_dump_session(
|
|
1730
|
+
target_path,
|
|
1731
|
+
fig=fig,
|
|
1732
|
+
ax=ax,
|
|
1733
|
+
x_data_list=x_data_list,
|
|
1734
|
+
y_data_list=y_data_list,
|
|
1735
|
+
orig_y=orig_y,
|
|
1736
|
+
offsets_list=offsets_list,
|
|
1737
|
+
labels=labels,
|
|
1738
|
+
delta=delta,
|
|
1739
|
+
args=args,
|
|
1740
|
+
tick_state=tick_state,
|
|
1741
|
+
cif_tick_series=(getattr(_bp, 'cif_tick_series', None) if _bp is not None else None),
|
|
1742
|
+
cif_hkl_map=(getattr(_bp, 'cif_hkl_map', None) if _bp is not None else None),
|
|
1743
|
+
cif_hkl_label_map=(getattr(_bp, 'cif_hkl_label_map', None) if _bp is not None else None),
|
|
1744
|
+
show_cif_hkl=(bool(getattr(_bp,'show_cif_hkl', False)) if _bp is not None else False),
|
|
1745
|
+
show_cif_titles=(bool(getattr(_bp,'show_cif_titles', True)) if _bp is not None else True),
|
|
1746
|
+
skip_confirm=skip_confirm,
|
|
1747
|
+
)
|
|
1748
|
+
print(f"Saved session to {target_path}")
|
|
1749
|
+
fig._last_session_save_path = target_path
|
|
1667
1750
|
except Exception as e:
|
|
1668
1751
|
print(f"Error saving session: {e}")
|
|
1669
1752
|
continue
|
|
@@ -3493,11 +3576,32 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3493
3576
|
print(f" {_i}: {fname} ({timestamp})")
|
|
3494
3577
|
else:
|
|
3495
3578
|
print(f" {_i}: {fname}")
|
|
3496
|
-
|
|
3579
|
+
last_style_path = getattr(fig, '_last_style_export_path', None)
|
|
3580
|
+
if last_style_path:
|
|
3581
|
+
sub = input(colorize_prompt("Style submenu: (e=export, o=overwrite last, q=return, r=refresh): ")).strip().lower()
|
|
3582
|
+
else:
|
|
3583
|
+
sub = input(colorize_prompt("Style submenu: (e=export, q=return, r=refresh): ")).strip().lower()
|
|
3497
3584
|
if sub == 'q':
|
|
3498
3585
|
break
|
|
3499
3586
|
if sub == 'r' or sub == '':
|
|
3500
3587
|
continue
|
|
3588
|
+
if sub == 'o':
|
|
3589
|
+
# Overwrite last exported style file
|
|
3590
|
+
if not last_style_path:
|
|
3591
|
+
print("No previous export found.")
|
|
3592
|
+
continue
|
|
3593
|
+
if not os.path.exists(last_style_path):
|
|
3594
|
+
print(f"Previous export file not found: {last_style_path}")
|
|
3595
|
+
continue
|
|
3596
|
+
yn = input(f"Overwrite '{os.path.basename(last_style_path)}'? (y/n): ").strip().lower()
|
|
3597
|
+
if yn != 'y':
|
|
3598
|
+
continue
|
|
3599
|
+
# Call export_style_config with overwrite_path to skip dialog
|
|
3600
|
+
exported_path = export_style_config(None, base_path=None, overwrite_path=last_style_path)
|
|
3601
|
+
if exported_path:
|
|
3602
|
+
fig._last_style_export_path = exported_path
|
|
3603
|
+
style_menu_active = False
|
|
3604
|
+
break
|
|
3501
3605
|
if sub == 'e':
|
|
3502
3606
|
save_base = choose_save_path(source_file_paths, purpose="style export")
|
|
3503
3607
|
if not save_base:
|
|
@@ -3505,7 +3609,9 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3505
3609
|
continue
|
|
3506
3610
|
print(f"\nChosen path: {save_base}")
|
|
3507
3611
|
# Call export_style_config which handles the entire export dialog
|
|
3508
|
-
export_style_config(None, base_path=save_base) # filename parameter ignored
|
|
3612
|
+
exported_path = export_style_config(None, base_path=save_base) # filename parameter ignored
|
|
3613
|
+
if exported_path:
|
|
3614
|
+
fig._last_style_export_path = exported_path
|
|
3509
3615
|
style_menu_active = False # Exit style submenu and return to main menu
|
|
3510
3616
|
break
|
|
3511
3617
|
else:
|
|
@@ -3543,14 +3649,33 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3543
3649
|
else:
|
|
3544
3650
|
print(f" {i}: {fname}")
|
|
3545
3651
|
|
|
3546
|
-
|
|
3652
|
+
last_figure_path = getattr(fig, '_last_figure_export_path', None)
|
|
3653
|
+
if last_figure_path:
|
|
3654
|
+
filename = input("Enter filename (default SVG if no extension), number to overwrite, or o to overwrite last (q=cancel): ").strip()
|
|
3655
|
+
else:
|
|
3656
|
+
filename = input("Enter filename (default SVG if no extension) or number to overwrite (q=cancel): ").strip()
|
|
3547
3657
|
if not filename or filename.lower() == 'q':
|
|
3548
3658
|
print("Canceled.")
|
|
3549
3659
|
continue
|
|
3550
3660
|
|
|
3661
|
+
already_confirmed = False # Initialize for new filename case
|
|
3662
|
+
# Check for 'o' option
|
|
3663
|
+
if filename.lower() == 'o':
|
|
3664
|
+
if not last_figure_path:
|
|
3665
|
+
print("No previous export found.")
|
|
3666
|
+
continue
|
|
3667
|
+
if not os.path.exists(last_figure_path):
|
|
3668
|
+
print(f"Previous export file not found: {last_figure_path}")
|
|
3669
|
+
continue
|
|
3670
|
+
yn = input(f"Overwrite '{os.path.basename(last_figure_path)}'? (y/n): ").strip().lower()
|
|
3671
|
+
if yn != 'y':
|
|
3672
|
+
print("Canceled.")
|
|
3673
|
+
continue
|
|
3674
|
+
export_target = last_figure_path
|
|
3675
|
+
already_confirmed = True
|
|
3551
3676
|
# Check if user selected a number
|
|
3552
|
-
|
|
3553
|
-
|
|
3677
|
+
elif filename.isdigit() and files:
|
|
3678
|
+
already_confirmed = False
|
|
3554
3679
|
idx = int(filename)
|
|
3555
3680
|
if 1 <= idx <= len(files):
|
|
3556
3681
|
name = files[idx-1]
|
|
@@ -3617,6 +3742,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3617
3742
|
else:
|
|
3618
3743
|
fig.savefig(export_target, dpi=300)
|
|
3619
3744
|
print(f"Figure saved to {export_target}")
|
|
3745
|
+
fig._last_figure_export_path = export_target
|
|
3620
3746
|
for i, txt in enumerate(label_text_objects):
|
|
3621
3747
|
txt.set_text(f"{i+1}: {labels[i]}")
|
|
3622
3748
|
fig.canvas.draw()
|
|
@@ -1716,13 +1716,31 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1716
1716
|
else:
|
|
1717
1717
|
print(f" {i}: {fname}")
|
|
1718
1718
|
|
|
1719
|
-
|
|
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()
|
|
1720
1724
|
if not fname or fname.lower() == 'q':
|
|
1721
1725
|
print_menu(); continue
|
|
1722
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
|
|
1723
1741
|
# Check if user selected a number
|
|
1724
|
-
|
|
1725
|
-
|
|
1742
|
+
elif fname.isdigit() and files:
|
|
1743
|
+
already_confirmed = False
|
|
1726
1744
|
idx = int(fname)
|
|
1727
1745
|
if 1 <= idx <= len(files):
|
|
1728
1746
|
name = files[idx-1]
|
|
@@ -1782,6 +1800,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1782
1800
|
else:
|
|
1783
1801
|
fig.savefig(target, dpi=300)
|
|
1784
1802
|
print(f"Exported figure to {target}")
|
|
1803
|
+
fig._last_figure_export_path = target
|
|
1785
1804
|
except Exception as e:
|
|
1786
1805
|
print(f"Export failed: {e}")
|
|
1787
1806
|
print_menu(); continue
|
|
@@ -1985,10 +2004,28 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1985
2004
|
print(f" {i}: {f} ({timestamp})")
|
|
1986
2005
|
else:
|
|
1987
2006
|
print(f" {i}: {f}")
|
|
1988
|
-
|
|
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): "
|
|
1989
2012
|
choice = input(prompt).strip()
|
|
1990
2013
|
if not choice or choice.lower() == 'q':
|
|
1991
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
|
|
1992
2029
|
if choice.isdigit() and files:
|
|
1993
2030
|
idx = int(choice)
|
|
1994
2031
|
if 1 <= idx <= len(files):
|
|
@@ -1997,10 +2034,13 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
1997
2034
|
if yn != 'y':
|
|
1998
2035
|
print_menu(); continue
|
|
1999
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
|
|
2000
2040
|
else:
|
|
2001
2041
|
print("Invalid number.")
|
|
2002
2042
|
print_menu(); continue
|
|
2003
|
-
|
|
2043
|
+
if choice.lower() != 'o':
|
|
2004
2044
|
name = choice
|
|
2005
2045
|
root, ext = os.path.splitext(name)
|
|
2006
2046
|
if ext == '':
|
|
@@ -2010,7 +2050,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
2010
2050
|
yn = input(f"'{os.path.basename(target)}' exists. Overwrite? (y/n): ").strip().lower()
|
|
2011
2051
|
if yn != 'y':
|
|
2012
2052
|
print_menu(); continue
|
|
2013
|
-
|
|
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
|
|
2014
2055
|
except Exception as e:
|
|
2015
2056
|
print(f"Save failed: {e}")
|
|
2016
2057
|
print_menu(); continue
|
|
@@ -3463,6 +3504,7 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3463
3504
|
else:
|
|
3464
3505
|
print(f" {_i}: {fname}")
|
|
3465
3506
|
|
|
3507
|
+
last_style_path = getattr(fig, '_last_style_export_path', None)
|
|
3466
3508
|
if ec_ax is None:
|
|
3467
3509
|
print("\nNote: Style export (.bps/.bpsg) is only available in dual-pane mode (with EC file).")
|
|
3468
3510
|
sub = input("Style submenu: (q=return, r=refresh): ").strip().lower()
|
|
@@ -3474,11 +3516,38 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3474
3516
|
print("Unknown choice.")
|
|
3475
3517
|
continue
|
|
3476
3518
|
else:
|
|
3477
|
-
|
|
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()
|
|
3478
3523
|
if sub == 'q':
|
|
3479
3524
|
break
|
|
3480
3525
|
if sub == 'r' or sub == '':
|
|
3481
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
|
|
3482
3551
|
if sub == 'e':
|
|
3483
3552
|
# Ask for ps or psg
|
|
3484
3553
|
print("Export options:")
|
|
@@ -3598,6 +3667,23 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3598
3667
|
'x': getattr(ec_ax.xaxis, 'labelpad', None),
|
|
3599
3668
|
'y': getattr(ec_ax.yaxis, 'labelpad', None),
|
|
3600
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
|
+
}
|
|
3601
3687
|
|
|
3602
3688
|
if exp_choice == 'ps':
|
|
3603
3689
|
cb_h_offset = getattr(cbar.ax, '_cb_h_offset_in', 0.0)
|
|
@@ -3607,8 +3693,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3607
3693
|
'version': 2,
|
|
3608
3694
|
'figure': {'canvas_size': [fig_w, fig_h], 'cb_visible': cb_visible, 'cb_label_mode': cb_label_mode},
|
|
3609
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},
|
|
3610
|
-
'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},
|
|
3611
|
-
'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},
|
|
3612
3698
|
'font': {'family': fam, 'size': fsize},
|
|
3613
3699
|
'colorbar': {'label': cb_label_text, 'mode': cb_label_mode, 'visible': cb_visible},
|
|
3614
3700
|
}
|
|
@@ -3621,8 +3707,8 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
3621
3707
|
'version': 2,
|
|
3622
3708
|
'figure': {'canvas_size': [fig_w, fig_h], 'cb_visible': cb_visible, 'cb_label_mode': cb_label_mode},
|
|
3623
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},
|
|
3624
|
-
'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},
|
|
3625
|
-
'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},
|
|
3626
3712
|
'font': {'family': fam, 'size': fsize},
|
|
3627
3713
|
'axes_geometry': _get_geometry_snapshot(ax, ec_ax),
|
|
3628
3714
|
'colorbar': {'label': cb_label_text, 'mode': cb_label_mode, 'visible': cb_visible},
|
|
@@ -4223,6 +4309,33 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
4223
4309
|
except Exception:
|
|
4224
4310
|
pass
|
|
4225
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
|
+
|
|
4226
4339
|
# Apply labelpads (title positioning) - preserve current if not in config
|
|
4227
4340
|
if version >= 2:
|
|
4228
4341
|
try:
|
|
@@ -4266,6 +4379,44 @@ def operando_ec_interactive_menu(fig, ax, im, cbar, ec_ax, file_paths=None):
|
|
|
4266
4379
|
except Exception as e:
|
|
4267
4380
|
print(f"Warning: Could not apply EC labelpads: {e}")
|
|
4268
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
|
+
|
|
4269
4420
|
# Final redraw
|
|
4270
4421
|
try:
|
|
4271
4422
|
fig.canvas.draw()
|