batplot 1.7.23__py3-none-any.whl → 1.7.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
batplot/style.py CHANGED
@@ -500,7 +500,8 @@ def export_style_config(
500
500
  label_text_objects: Optional[List] = None,
501
501
  base_path: Optional[str] = None,
502
502
  show_cif_titles: Optional[bool] = None,
503
- ) -> None:
503
+ overwrite_path: Optional[str] = None,
504
+ ) -> Optional[str]:
504
505
  """Export style configuration after displaying a summary and prompting the user.
505
506
 
506
507
  This function now matches the EC menu workflow: display summary, then prompt for export.
@@ -635,6 +636,14 @@ def export_style_config(
635
636
  "has_left_y": bool(ax.yaxis.label.get_visible()),
636
637
  }
637
638
  cfg["axis_title_texts"] = axis_title_texts
639
+ cfg["title_offsets"] = {
640
+ "top_y": float(getattr(ax, '_top_xlabel_manual_offset_y_pts', 0.0) or 0.0),
641
+ "top_x": float(getattr(ax, '_top_xlabel_manual_offset_x_pts', 0.0) or 0.0),
642
+ "bottom_y": float(getattr(ax, '_bottom_xlabel_manual_offset_y_pts', 0.0) or 0.0),
643
+ "left_x": float(getattr(ax, '_left_ylabel_manual_offset_x_pts', 0.0) or 0.0),
644
+ "right_x": float(getattr(ax, '_right_ylabel_manual_offset_x_pts', 0.0) or 0.0),
645
+ "right_y": float(getattr(ax, '_right_ylabel_manual_offset_y_pts', 0.0) or 0.0),
646
+ }
638
647
  # Save rotation angle
639
648
  cfg["rotation_angle"] = getattr(ax, '_rotation_angle', 0)
640
649
 
@@ -674,14 +683,27 @@ def export_style_config(
674
683
  if serialized_palettes:
675
684
  cfg['curve_palettes'] = serialized_palettes
676
685
 
677
- # Ask user for style-only or style+geometry
678
- print("\nExport options:")
679
- print(" ps = style only (.bps)")
680
- print(" psg = style + geometry (.bpsg)")
681
- exp_choice = input("Export choice (ps/psg, q=cancel): ").strip().lower()
682
- if not exp_choice or exp_choice == 'q':
683
- print("Style export canceled.")
684
- return
686
+ # If overwrite_path is provided, determine export type from existing file
687
+ if overwrite_path:
688
+ try:
689
+ with open(overwrite_path, 'r', encoding='utf-8') as f:
690
+ old_cfg = json.load(f)
691
+ old_kind = old_cfg.get('kind', '')
692
+ if old_kind == 'xy_style_geom':
693
+ exp_choice = 'psg'
694
+ else:
695
+ exp_choice = 'ps'
696
+ except Exception:
697
+ exp_choice = 'ps' # Default to style-only if can't read
698
+ else:
699
+ # Ask user for style-only or style+geometry
700
+ print("\nExport options:")
701
+ print(" ps = style only (.bps)")
702
+ print(" psg = style + geometry (.bpsg)")
703
+ exp_choice = input("Export choice (ps/psg, q=cancel): ").strip().lower()
704
+ if not exp_choice or exp_choice == 'q':
705
+ print("Style export canceled.")
706
+ return None
685
707
 
686
708
  # Determine file extension and add geometry if requested
687
709
  if exp_choice == 'ps':
@@ -704,55 +726,74 @@ def export_style_config(
704
726
  print(f"Unknown option: {exp_choice}")
705
727
  return
706
728
 
707
- # List existing files for user convenience (from Styles subdirectory)
708
- import os
709
- from .utils import list_files_in_subdirectory, get_organized_path
710
-
711
- if base_path:
712
- print(f"\nChosen path: {base_path}")
713
- file_list = list_files_in_subdirectory((default_ext, '.bpcfg'), 'style', base_path=base_path)
714
- style_files = [f[0] for f in file_list]
715
-
716
- if style_files:
717
- styles_root = base_path if base_path else os.getcwd()
718
- styles_dir = os.path.join(styles_root, 'Styles')
719
- print(f"\nExisting {default_ext} files in {styles_dir}:")
720
- for i, f in enumerate(style_files, 1):
721
- print(f" {i}: {f}")
729
+ # If overwrite_path is provided, use it directly
730
+ if overwrite_path:
731
+ target_path = overwrite_path
732
+ else:
733
+ # List existing files for user convenience (from Styles subdirectory)
734
+ import os
735
+ from .utils import list_files_in_subdirectory, get_organized_path
736
+
737
+ if base_path:
738
+ print(f"\nChosen path: {base_path}")
739
+ file_list = list_files_in_subdirectory((default_ext, '.bpcfg'), 'style', base_path=base_path)
740
+ style_files = [f[0] for f in file_list]
722
741
 
723
- choice = input("Export to file? Enter filename or number to overwrite (q=cancel): ").strip()
724
- if not choice or choice.lower() == 'q':
725
- print("Style export canceled.")
726
- return
742
+ if style_files:
743
+ styles_root = base_path if base_path else os.getcwd()
744
+ styles_dir = os.path.join(styles_root, 'Styles')
745
+ print(f"\nExisting {default_ext} files in {styles_dir}:")
746
+ for i, f in enumerate(style_files, 1):
747
+ print(f" {i}: {f}")
727
748
 
728
- # Determine the target path
729
- if choice.isdigit() and style_files and 1 <= int(choice) <= len(style_files):
730
- target_path = file_list[int(choice) - 1][1] # Full path from list
731
- else:
732
- # Add default extension if no extension provided
733
- if not any(choice.lower().endswith(ext) for ext in ['.bps', '.bpsg', '.bpcfg']):
734
- filename_with_ext = f"{choice}{default_ext}"
749
+ last_style_path = getattr(fig, '_last_style_export_path', None)
750
+ if last_style_path:
751
+ choice = input("Export to file? Enter filename, number to overwrite, or o to overwrite last (q=cancel): ").strip()
735
752
  else:
736
- filename_with_ext = choice
737
-
738
- # Use organized path unless it's an absolute path
739
- if os.path.isabs(filename_with_ext):
740
- target_path = filename_with_ext
753
+ choice = input("Export to file? Enter filename or number to overwrite (q=cancel): ").strip()
754
+ if not choice or choice.lower() == 'q':
755
+ print("Style export canceled.")
756
+ return None
757
+ if choice.lower() == 'o':
758
+ # Overwrite last exported style file - handled by caller
759
+ if not last_style_path:
760
+ print("No previous export found.")
761
+ return None
762
+ if not os.path.exists(last_style_path):
763
+ print(f"Previous export file not found: {last_style_path}")
764
+ return None
765
+ target_path = last_style_path
741
766
  else:
742
- target_path = get_organized_path(filename_with_ext, 'style', base_path=base_path)
767
+ # Determine the target path
768
+ if choice.isdigit() and style_files and 1 <= int(choice) <= len(style_files):
769
+ target_path = file_list[int(choice) - 1][1] # Full path from list
770
+ else:
771
+ # Add default extension if no extension provided
772
+ if not any(choice.lower().endswith(ext) for ext in ['.bps', '.bpsg', '.bpcfg']):
773
+ filename_with_ext = f"{choice}{default_ext}"
774
+ else:
775
+ filename_with_ext = choice
776
+
777
+ # Use organized path unless it's an absolute path
778
+ if os.path.isabs(filename_with_ext):
779
+ target_path = filename_with_ext
780
+ else:
781
+ target_path = get_organized_path(filename_with_ext, 'style', base_path=base_path)
743
782
 
744
- # Only prompt ONCE for overwrite if the file exists
745
- if os.path.exists(target_path):
746
- yn = input(f"Overwrite '{os.path.basename(target_path)}'? (y/n): ").strip().lower()
747
- if yn != 'y':
748
- print("Style export canceled.")
749
- return
783
+ # Only prompt ONCE for overwrite if the file exists
784
+ if os.path.exists(target_path):
785
+ yn = input(f"Overwrite '{os.path.basename(target_path)}'? (y/n): ").strip().lower()
786
+ if yn != 'y':
787
+ print("Style export canceled.")
788
+ return None
750
789
 
751
790
  with open(target_path, "w", encoding="utf-8") as f:
752
791
  json.dump(cfg, f, indent=2)
753
792
  print(f"Exported style to {target_path}")
793
+ return target_path
754
794
  except Exception as e:
755
795
  print(f"Error exporting style: {e}")
796
+ return None
756
797
 
757
798
 
758
799
  def apply_style_config(
@@ -1204,6 +1245,27 @@ def apply_style_config(
1204
1245
  except Exception as e:
1205
1246
  print(f"Warning: Could not restore rotation angle: {e}")
1206
1247
 
1248
+ # Restore title offsets BEFORE positioning titles
1249
+ title_offsets = cfg.get("title_offsets", {})
1250
+ if title_offsets:
1251
+ try:
1252
+ if 'top_y' in title_offsets:
1253
+ ax._top_xlabel_manual_offset_y_pts = float(title_offsets.get('top_y', 0.0) or 0.0)
1254
+ else:
1255
+ # Backward compatibility: old format used 'top' for y-offset
1256
+ ax._top_xlabel_manual_offset_y_pts = float(title_offsets.get('top', 0.0) or 0.0)
1257
+ ax._top_xlabel_manual_offset_x_pts = float(title_offsets.get('top_x', 0.0) or 0.0)
1258
+ ax._bottom_xlabel_manual_offset_y_pts = float(title_offsets.get('bottom_y', 0.0) or 0.0)
1259
+ ax._left_ylabel_manual_offset_x_pts = float(title_offsets.get('left_x', 0.0) or 0.0)
1260
+ if 'right_x' in title_offsets:
1261
+ ax._right_ylabel_manual_offset_x_pts = float(title_offsets.get('right_x', 0.0) or 0.0)
1262
+ else:
1263
+ # Backward compatibility: old format used 'right' for x-offset
1264
+ ax._right_ylabel_manual_offset_x_pts = float(title_offsets.get('right', 0.0) or 0.0)
1265
+ ax._right_ylabel_manual_offset_y_pts = float(title_offsets.get('right_y', 0.0) or 0.0)
1266
+ except Exception as e:
1267
+ print(f"Warning: Could not restore title offsets: {e}")
1268
+
1207
1269
  # Restore grid state
1208
1270
  if "grid" in cfg:
1209
1271
  try:
@@ -1,9 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.7.23
3
+ Version: 1.7.25
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
- License-Expression: MIT
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Tian Dai
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
7
28
  Project-URL: Homepage, https://github.com/TianDai1729/batplot
8
29
  Project-URL: Repository, https://github.com/TianDai1729/batplot
9
30
  Project-URL: Issues, https://github.com/TianDai1729/batplot/issues
@@ -1,28 +1,28 @@
1
- batplot/__init__.py,sha256=vkOVAS6v23TnQZXUW0hx9pGUWLY7MUF__AXzrydzgBs,119
1
+ batplot/__init__.py,sha256=2qXM_ch4J6g2wG6kjsgmtes4tumgb-iIPYbAIvlfOM0,119
2
2
  batplot/args.py,sha256=6g1eHVsycDSAenwrXuCTcyuXCy_-30zy1lWtdguBG2s,34983
3
3
  batplot/batch.py,sha256=YQ7obCIqLCObwDbM7TXpOBh7g7BO95wZNsa2Fy84c6o,53858
4
- batplot/batplot.py,sha256=h960XKSmJ825DqI99Xnyomg_gaB0aFDUIKxkovm8BPQ,170691
4
+ batplot/batplot.py,sha256=8cSAOZphoecINZMpYuetq-rrSyg_zmRfV08VtvX4_B8,170723
5
5
  batplot/cif.py,sha256=JfHwNf3SHrcpALc_F5NjJmQ3lg71MBRSaIUJjGYPTx8,30120
6
6
  batplot/cli.py,sha256=ScDb2je8VQ0mz_z0SLCHEigiTuFPY5pb1snnzCouKms,5828
7
7
  batplot/color_utils.py,sha256=ow2ElqjIWFLRdrnLwQvrnfa3w3IEB0FodPFdoDQR_Dc,19990
8
8
  batplot/config.py,sha256=6nGY7fKN4T5KZUGQS2ArUBgEkLAL0j37XwG5SCVQgKA,6420
9
9
  batplot/converters.py,sha256=rR2WMPM0nR5E3eZI3gWbaJf_AfbdQx3urVSbJmZXNzo,8237
10
- batplot/cpc_interactive.py,sha256=ICrY25rSqKsds9Cb2lW1GiTdY-lTQaAuD4NRzQCZGCw,187304
11
- batplot/electrochem_interactive.py,sha256=f71CsXDNoge-7ENKX3PhmnSf7uBFXoHhcPSHL9_cF-Q,209167
12
- batplot/interactive.py,sha256=gAmfHLVu4dAC_-hpmpfJj8dyQTBbzRNweVODuQDIocw,196317
10
+ batplot/cpc_interactive.py,sha256=90c-Rd9zcLIsL7mBb3gBDw4AnaWd9KnDtln9mhVKhNo,225252
11
+ batplot/electrochem_interactive.py,sha256=S7UmgZAMCD0sRGPW7xRxwOaHV5_vkuwxZSegWJHxJxI,217258
12
+ batplot/interactive.py,sha256=u0rDbxfjL3LHpAPUL3t6zUp94e-fJEA5_ApXTFfHflA,203616
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=snGzZBlgmXhvGupaMv-1Jvb9V7E3APMadYXKfQ4rZB4,281889
16
+ batplot/operando_ec_interactive.py,sha256=a9OpAguBznUf9p642OyIzya-1AQJKWUO4pqjo7JNHEQ,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=3oo0jimTEdK7HpcBb326F8iBCM_LKPwNaSnmqSKU92Y,121025
20
- batplot/style.py,sha256=wmg4D4LdunLU88YYzh9y2REwet6xc5Ox2naa1ZW2OlM,58276
19
+ batplot/session.py,sha256=1DxwzxnTD_w9nrUWi-aIsBd_3bgfo4xchQGe0nd71Dg,131533
20
+ batplot/style.py,sha256=ZeRJ15fneSQqCYN_SGap-9Y2J8UkzRxYmf6qLJA6zMo,62058
21
21
  batplot/ui.py,sha256=MIY2x_ghCYxjdYhjMUZsMMnQEUBLgrIT37hfPGZf_cs,36320
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.23.dist-info/licenses/LICENSE,sha256=2PAnHeCiTfgI7aKZLWr0G56HI9fGKQ0CEbQ02H-yExQ,1065
25
+ batplot-1.7.25.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.23.dist-info/METADATA,sha256=9CLx5Pt0Lrcu3244DqSt7DjHP8zO9FfqsGh-nFmvtfs,6188
49
- batplot-1.7.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
- batplot-1.7.23.dist-info/entry_points.txt,sha256=73GgH3Zs-qGIvgiyQLgGsSW-ryOwPPKHveOW6TDIR5Q,82
51
- batplot-1.7.23.dist-info/top_level.txt,sha256=CgqK4RpsYnUFAcqO4bLOnEhCoPY4IPEGLPkiDlzLIxg,39
52
- batplot-1.7.23.dist-info/RECORD,,
48
+ batplot-1.7.25.dist-info/METADATA,sha256=AyVvcD6JyoG0Bl2AAvNtw0YVI9P6vHZAhqFJQQu-KYM,7407
49
+ batplot-1.7.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
+ batplot-1.7.25.dist-info/entry_points.txt,sha256=73GgH3Zs-qGIvgiyQLgGsSW-ryOwPPKHveOW6TDIR5Q,82
51
+ batplot-1.7.25.dist-info/top_level.txt,sha256=CgqK4RpsYnUFAcqO4bLOnEhCoPY4IPEGLPkiDlzLIxg,39
52
+ batplot-1.7.25.dist-info/RECORD,,