batplot 1.8.51__tar.gz → 1.8.52__tar.gz

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.
Files changed (322) hide show
  1. {batplot-1.8.51 → batplot-1.8.52}/PKG-INFO +1 -1
  2. {batplot-1.8.51 → batplot-1.8.52}/batplot/__init__.py +1 -1
  3. {batplot-1.8.51 → batplot-1.8.52}/batplot/cli_save.py +3 -2
  4. {batplot-1.8.51 → batplot-1.8.52}/batplot/data/CHANGELOG.md +4 -0
  5. {batplot-1.8.51 → batplot-1.8.52}/batplot/data/latest_release_notes.json +1 -1
  6. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_io.py +28 -6
  7. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_panel_state.py +13 -0
  8. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_scoped_sync.py +4 -3
  9. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/common.py +45 -0
  10. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/cpc_batch_helpers.py +68 -50
  11. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/ec_batch_helpers.py +120 -55
  12. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/load.py +7 -3
  13. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_cpc.py +107 -36
  14. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_dqdv_2d.py +35 -3
  15. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_ec.py +38 -3
  16. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_histo.py +45 -11
  17. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_operando.py +46 -12
  18. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/menu_xy.py +203 -42
  19. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/operando_batch_helpers.py +194 -80
  20. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/xy_batch_helpers.py +53 -27
  21. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/axis_state.py +56 -6
  22. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/interactive_state.py +15 -17
  23. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/menus.py +25 -21
  24. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/session_helpers.py +45 -2
  25. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/sources.py +13 -9
  26. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/spines.py +138 -57
  27. batplot-1.8.52/batplot/plot_modes/common/title_offsets.py +172 -0
  28. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/actions.py +149 -167
  29. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/add_file.py +10 -3
  30. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/colors.py +91 -26
  31. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/interactive.py +28 -16
  32. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/labels.py +41 -12
  33. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/legend.py +13 -6
  34. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/panel_menus.py +133 -77
  35. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/routing.py +3 -2
  36. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/session.py +195 -144
  37. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/snapshots.py +39 -15
  38. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/style.py +71 -31
  39. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/wasd_menu.py +60 -41
  40. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/actions.py +52 -19
  41. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/colors.py +71 -28
  42. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/dqdv_2d.py +196 -27
  43. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/dual_axis_menu.py +66 -21
  44. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/interactive.py +27 -8
  45. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/labels.py +6 -2
  46. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/legend.py +12 -8
  47. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/legend_order.py +6 -0
  48. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/line_style.py +31 -28
  49. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/menu.py +9 -3
  50. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/overview.py +19 -3
  51. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/routing.py +6 -4
  52. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/session.py +162 -89
  53. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/smoothing_menu.py +18 -0
  54. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/spine_colors.py +27 -12
  55. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/style.py +76 -71
  56. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/style_apply.py +67 -73
  57. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/undo_state.py +132 -15
  58. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/actions.py +32 -11
  59. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/colors.py +35 -6
  60. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/density_curve.py +27 -1
  61. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/interactive.py +62 -21
  62. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/labels.py +25 -10
  63. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/line_style.py +13 -12
  64. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/plot.py +7 -4
  65. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/session.py +20 -4
  66. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/spines.py +51 -21
  67. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/y_range.py +7 -5
  68. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/actions.py +30 -11
  69. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/axes_limits_menu.py +38 -12
  70. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/axis_units.py +11 -0
  71. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/cif_menu.py +30 -14
  72. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/colors.py +29 -8
  73. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/intensity_menu.py +37 -12
  74. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/interactive.py +137 -110
  75. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/labels.py +92 -37
  76. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/layout.py +27 -7
  77. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/line_style.py +13 -7
  78. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/peaks.py +24 -3
  79. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/plot.py +20 -0
  80. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/routing.py +3 -2
  81. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/session.py +261 -94
  82. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/spine_colors.py +87 -8
  83. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/style.py +47 -30
  84. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/style_apply.py +309 -163
  85. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/undo_state.py +95 -33
  86. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/visibility.py +51 -6
  87. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/actions.py +30 -23
  88. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/arrange.py +7 -0
  89. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/axis_range.py +120 -25
  90. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/axis_units.py +17 -0
  91. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/cif.py +43 -23
  92. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/colors.py +27 -7
  93. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/derivative.py +16 -8
  94. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/interactive.py +162 -50
  95. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/labels.py +20 -4
  96. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/line_style.py +38 -17
  97. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/offset_menu.py +8 -0
  98. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/peaks.py +11 -3
  99. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/pipeline.py +37 -22
  100. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/session.py +190 -135
  101. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/smoothing.py +40 -0
  102. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/spines.py +88 -22
  103. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/style.py +164 -85
  104. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/undo_state.py +246 -33
  105. {batplot-1.8.51 → batplot-1.8.52}/batplot/ui.py +341 -67
  106. {batplot-1.8.51 → batplot-1.8.52}/batplot/utils.py +5 -0
  107. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/PKG-INFO +1 -1
  108. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/SOURCES.txt +34 -1
  109. {batplot-1.8.51 → batplot-1.8.52}/pyproject.toml +1 -1
  110. {batplot-1.8.51 → batplot-1.8.52}/tests/test_all_menus_smoke.py +7 -1
  111. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_all_modes_sync.py +9 -2
  112. batplot-1.8.52/tests/test_batch_geometry_pisb_keys.py +186 -0
  113. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_scoped_sync_modes.py +3 -3
  114. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_session.py +5 -2
  115. batplot-1.8.52/tests/test_batch_style_pisb_keys.py +267 -0
  116. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cross_platform_parity.py +34 -0
  117. batplot-1.8.52/tests/test_deep_residual_pisb_gates.py +268 -0
  118. batplot-1.8.52/tests/test_deep_round10_bc_safe.py +52 -0
  119. batplot-1.8.52/tests/test_deep_round11_title_fidelity.py +103 -0
  120. batplot-1.8.52/tests/test_deep_round12_commercial_gates.py +122 -0
  121. batplot-1.8.52/tests/test_deep_round13_final_key_audit.py +138 -0
  122. batplot-1.8.52/tests/test_deep_round3_pisb_gates.py +225 -0
  123. batplot-1.8.52/tests/test_deep_round4_pisb_gates.py +145 -0
  124. batplot-1.8.52/tests/test_deep_round5_pisb_gates.py +168 -0
  125. batplot-1.8.52/tests/test_deep_round6_session_fidelity.py +136 -0
  126. batplot-1.8.52/tests/test_deep_round7_root_helpers.py +120 -0
  127. batplot-1.8.52/tests/test_deep_round8_root_helpers.py +104 -0
  128. batplot-1.8.52/tests/test_deep_round9_root_helpers.py +88 -0
  129. batplot-1.8.52/tests/test_ec_cpc_geometry_pisb_keys.py +486 -0
  130. batplot-1.8.52/tests/test_ec_cpc_style_pisb_keys.py +296 -0
  131. batplot-1.8.52/tests/test_every_key_behavioral_walk.py +562 -0
  132. batplot-1.8.52/tests/test_final_audit_residuals.py +142 -0
  133. batplot-1.8.52/tests/test_full_menu_audit_gaps.py +145 -0
  134. batplot-1.8.52/tests/test_hard_check_twin_and_batch.py +151 -0
  135. batplot-1.8.52/tests/test_histo_geometry_pisb_keys.py +274 -0
  136. batplot-1.8.52/tests/test_histo_style_pisb_keys.py +299 -0
  137. batplot-1.8.52/tests/test_interactive_handler_intent.py +250 -0
  138. {batplot-1.8.51 → batplot-1.8.52}/tests/test_interactive_menu_smoke.py +87 -13
  139. {batplot-1.8.51 → batplot-1.8.52}/tests/test_interactive_state.py +52 -0
  140. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_cif_add.py +8 -7
  141. batplot-1.8.52/tests/test_operando_geometry_pisb_keys.py +265 -0
  142. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_roundtrip.py +15 -4
  143. batplot-1.8.52/tests/test_operando_spine_isolation.py +144 -0
  144. batplot-1.8.52/tests/test_operando_style_pisb_keys.py +213 -0
  145. {batplot-1.8.51 → batplot-1.8.52}/tests/test_pisb_deep_all_modes.py +12 -2
  146. batplot-1.8.52/tests/test_session_save_integrity.py +240 -0
  147. batplot-1.8.52/tests/test_spine_wasd_pisb_all_modes.py +435 -0
  148. batplot-1.8.52/tests/test_stupid_input_hardening.py +88 -0
  149. batplot-1.8.52/tests/test_style_import_integrity.py +144 -0
  150. batplot-1.8.52/tests/test_undo_stack_integrity.py +201 -0
  151. batplot-1.8.52/tests/test_xy_geometry_pisb_keys.py +372 -0
  152. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_modules.py +7 -1
  153. batplot-1.8.52/tests/test_xy_style_pisb_keys.py +339 -0
  154. batplot-1.8.51/batplot/plot_modes/common/title_offsets.py +0 -65
  155. {batplot-1.8.51 → batplot-1.8.52}/DEVELOPING.md +0 -0
  156. {batplot-1.8.51 → batplot-1.8.52}/LICENSE +0 -0
  157. {batplot-1.8.51 → batplot-1.8.52}/MANIFEST.in +0 -0
  158. {batplot-1.8.51 → batplot-1.8.52}/NOTICE +0 -0
  159. {batplot-1.8.51 → batplot-1.8.52}/README.md +0 -0
  160. {batplot-1.8.51 → batplot-1.8.52}/batplot/_mpl_backend.py +0 -0
  161. {batplot-1.8.51 → batplot-1.8.52}/batplot/args.py +0 -0
  162. {batplot-1.8.51 → batplot-1.8.52}/batplot/batch.py +0 -0
  163. {batplot-1.8.51 → batplot-1.8.52}/batplot/batplot.py +0 -0
  164. {batplot-1.8.51 → batplot-1.8.52}/batplot/canvas_interactive.py +0 -0
  165. {batplot-1.8.51 → batplot-1.8.52}/batplot/cif.py +0 -0
  166. {batplot-1.8.51 → batplot-1.8.52}/batplot/cli.py +0 -0
  167. {batplot-1.8.51 → batplot-1.8.52}/batplot/color_utils.py +0 -0
  168. {batplot-1.8.51 → batplot-1.8.52}/batplot/config.py +0 -0
  169. {batplot-1.8.51 → batplot-1.8.52}/batplot/converters.py +0 -0
  170. {batplot-1.8.51 → batplot-1.8.52}/batplot/dev_upgrade.py +0 -0
  171. {batplot-1.8.51 → batplot-1.8.52}/batplot/ec_common.py +0 -0
  172. {batplot-1.8.51 → batplot-1.8.52}/batplot/modes.py +0 -0
  173. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/__init__.py +0 -0
  174. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/__init__.py +0 -0
  175. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_commands.py +0 -0
  176. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_crosshair.py +0 -0
  177. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_figure_io.py +0 -0
  178. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_geom_helpers.py +0 -0
  179. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_menu_helpers.py +0 -0
  180. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/batch_menu_io.py +0 -0
  181. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/dqdv_2d_batch_helpers.py +0 -0
  182. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/histo_batch_helpers.py +0 -0
  183. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/kinds.py +0 -0
  184. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/batch_session/routing.py +0 -0
  185. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/__init__.py +0 -0
  186. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/batch_font.py +0 -0
  187. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/crosshair_export.py +0 -0
  188. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/files.py +0 -0
  189. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/font_extras.py +0 -0
  190. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/fonts.py +0 -0
  191. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/line_dash.py +0 -0
  192. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/menu_rendering.py +0 -0
  193. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/overview_metrics.py +0 -0
  194. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/palettes.py +0 -0
  195. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/session_data_guarantee.py +0 -0
  196. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/session_key_smoke.py +0 -0
  197. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/size_spec.py +0 -0
  198. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/smoothing.py +0 -0
  199. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/state_capture.py +0 -0
  200. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/style_routing.py +0 -0
  201. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/common/terminal.py +0 -0
  202. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/__init__.py +0 -0
  203. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/legend_order.py +0 -0
  204. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/load.py +0 -0
  205. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/menu.py +0 -0
  206. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/cpc/overview.py +0 -0
  207. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/__init__.py +0 -0
  208. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/capacity_cum.py +0 -0
  209. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/electrochem/export.py +0 -0
  210. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/__init__.py +0 -0
  211. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/fonts.py +0 -0
  212. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/load.py +0 -0
  213. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/routing.py +0 -0
  214. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/toggles.py +0 -0
  215. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/histo/wizard.py +0 -0
  216. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/__init__.py +0 -0
  217. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/grid.py +0 -0
  218. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/ions_axis.py +0 -0
  219. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/layout_menu.py +0 -0
  220. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/operando/menu.py +0 -0
  221. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/session_routing.py +0 -0
  222. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/__init__.py +0 -0
  223. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/data_ops.py +0 -0
  224. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/full_data.py +0 -0
  225. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/game.py +0 -0
  226. {batplot-1.8.51 → batplot-1.8.52}/batplot/plot_modes/xy/menu.py +0 -0
  227. {batplot-1.8.51 → batplot-1.8.52}/batplot/plotting.py +0 -0
  228. {batplot-1.8.51 → batplot-1.8.52}/batplot/readers.py +0 -0
  229. {batplot-1.8.51 → batplot-1.8.52}/batplot/readers_common.py +0 -0
  230. {batplot-1.8.51 → batplot-1.8.52}/batplot/readers_ec.py +0 -0
  231. {batplot-1.8.51 → batplot-1.8.52}/batplot/readers_xrd.py +0 -0
  232. {batplot-1.8.51 → batplot-1.8.52}/batplot/screen_color.py +0 -0
  233. {batplot-1.8.51 → batplot-1.8.52}/batplot/session.py +0 -0
  234. {batplot-1.8.51 → batplot-1.8.52}/batplot/showcol.py +0 -0
  235. {batplot-1.8.51 → batplot-1.8.52}/batplot/strip_header.py +0 -0
  236. {batplot-1.8.51 → batplot-1.8.52}/batplot/style.py +0 -0
  237. {batplot-1.8.51 → batplot-1.8.52}/batplot/version_check.py +0 -0
  238. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/dependency_links.txt +0 -0
  239. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/entry_points.txt +0 -0
  240. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/requires.txt +0 -0
  241. {batplot-1.8.51 → batplot-1.8.52}/batplot.egg-info/top_level.txt +0 -0
  242. {batplot-1.8.51 → batplot-1.8.52}/setup.cfg +0 -0
  243. {batplot-1.8.51 → batplot-1.8.52}/setup.py +0 -0
  244. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_crosshair.py +0 -0
  245. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_ec_cycles_status.py +0 -0
  246. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_font_menu.py +0 -0
  247. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_geom_menu.py +0 -0
  248. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_menu_parity.py +0 -0
  249. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_panel_status.py +0 -0
  250. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_pisb_contract.py +0 -0
  251. {batplot-1.8.51 → batplot-1.8.52}/tests/test_batch_spine_sync.py +0 -0
  252. {batplot-1.8.51 → batplot-1.8.52}/tests/test_bc_new_fields_defaults.py +0 -0
  253. {batplot-1.8.51 → batplot-1.8.52}/tests/test_bruker_intensity.py +0 -0
  254. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cif_tick_crystallography.py +0 -0
  255. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cli_all_flags.py +0 -0
  256. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cli_save.py +0 -0
  257. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cli_smoke.py +0 -0
  258. {batplot-1.8.51 → batplot-1.8.52}/tests/test_color_display_hex.py +0 -0
  259. {batplot-1.8.51 → batplot-1.8.52}/tests/test_color_listing_visible.py +0 -0
  260. {batplot-1.8.51 → batplot-1.8.52}/tests/test_color_utils.py +0 -0
  261. {batplot-1.8.51 → batplot-1.8.52}/tests/test_common_files.py +0 -0
  262. {batplot-1.8.51 → batplot-1.8.52}/tests/test_common_palettes.py +0 -0
  263. {batplot-1.8.51 → batplot-1.8.52}/tests/test_contracts.py +0 -0
  264. {batplot-1.8.51 → batplot-1.8.52}/tests/test_convert_xrd.py +0 -0
  265. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cpc_add_file.py +0 -0
  266. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cpc_legend_order.py +0 -0
  267. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cpc_roundtrip.py +0 -0
  268. {batplot-1.8.51 → batplot-1.8.52}/tests/test_critical_stability_fixes.py +0 -0
  269. {batplot-1.8.51 → batplot-1.8.52}/tests/test_cross_os_oe_paths.py +0 -0
  270. {batplot-1.8.51 → batplot-1.8.52}/tests/test_crosshair_export.py +0 -0
  271. {batplot-1.8.51 → batplot-1.8.52}/tests/test_csv_readers.py +0 -0
  272. {batplot-1.8.51 → batplot-1.8.52}/tests/test_deep_noninteractive_bugs.py +0 -0
  273. {batplot-1.8.51 → batplot-1.8.52}/tests/test_default_sizes_and_oe_persistence.py +0 -0
  274. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dev_upgrade.py +0 -0
  275. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dqdv_contour_session_keys.py +0 -0
  276. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dqdv_smooth_status.py +0 -0
  277. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dual_axis_menu_audit.py +0 -0
  278. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dual_mode_pisb.py +0 -0
  279. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dual_pisb_hard_gates.py +0 -0
  280. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dual_tk_a_coupling.py +0 -0
  281. {batplot-1.8.51 → batplot-1.8.52}/tests/test_dual_top_wasd_both_layers.py +0 -0
  282. {batplot-1.8.51 → batplot-1.8.52}/tests/test_ec_cycle_identity_roundtrip.py +0 -0
  283. {batplot-1.8.51 → batplot-1.8.52}/tests/test_ec_multifile_color_menu.py +0 -0
  284. {batplot-1.8.51 → batplot-1.8.52}/tests/test_ec_roundtrip.py +0 -0
  285. {batplot-1.8.51 → batplot-1.8.52}/tests/test_ec_session_bc_new_keys.py +0 -0
  286. {batplot-1.8.51 → batplot-1.8.52}/tests/test_gc_cumulative.py +0 -0
  287. {batplot-1.8.51 → batplot-1.8.52}/tests/test_histo.py +0 -0
  288. {batplot-1.8.51 → batplot-1.8.52}/tests/test_histo_interactive_imports.py +0 -0
  289. {batplot-1.8.51 → batplot-1.8.52}/tests/test_imk_stderr_guard.py +0 -0
  290. {batplot-1.8.51 → batplot-1.8.52}/tests/test_line_dash_styles.py +0 -0
  291. {batplot-1.8.51 → batplot-1.8.52}/tests/test_menu_handler_imports.py +0 -0
  292. {batplot-1.8.51 → batplot-1.8.52}/tests/test_menu_prompt_order.py +0 -0
  293. {batplot-1.8.51 → batplot-1.8.52}/tests/test_mpl_backend.py +0 -0
  294. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_axis_units.py +0 -0
  295. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_batch_menu.py +0 -0
  296. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_cif_color.py +0 -0
  297. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_layout_menu.py +0 -0
  298. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_peaks_axis_units.py +0 -0
  299. {batplot-1.8.51 → batplot-1.8.52}/tests/test_operando_submenu_smoke.py +0 -0
  300. {batplot-1.8.51 → batplot-1.8.52}/tests/test_overview_metrics.py +0 -0
  301. {batplot-1.8.51 → batplot-1.8.52}/tests/test_pi_axis_units_persistence.py +0 -0
  302. {batplot-1.8.51 → batplot-1.8.52}/tests/test_recent_axis_names.py +0 -0
  303. {batplot-1.8.51 → batplot-1.8.52}/tests/test_screen_color.py +0 -0
  304. {batplot-1.8.51 → batplot-1.8.52}/tests/test_screen_color_pisb.py +0 -0
  305. {batplot-1.8.51 → batplot-1.8.52}/tests/test_secondary_spine_colors.py +0 -0
  306. {batplot-1.8.51 → batplot-1.8.52}/tests/test_session_backward_compat.py +0 -0
  307. {batplot-1.8.51 → batplot-1.8.52}/tests/test_session_font_restore.py +0 -0
  308. {batplot-1.8.51 → batplot-1.8.52}/tests/test_session_full_data_guarantee.py +0 -0
  309. {batplot-1.8.51 → batplot-1.8.52}/tests/test_session_style_restore_parity.py +0 -0
  310. {batplot-1.8.51 → batplot-1.8.52}/tests/test_spine_colors_pisb.py +0 -0
  311. {batplot-1.8.51 → batplot-1.8.52}/tests/test_state_capture.py +0 -0
  312. {batplot-1.8.51 → batplot-1.8.52}/tests/test_strip_header.py +0 -0
  313. {batplot-1.8.51 → batplot-1.8.52}/tests/test_style_routing.py +0 -0
  314. {batplot-1.8.51 → batplot-1.8.52}/tests/test_tick_visibility_save_truth.py +0 -0
  315. {batplot-1.8.51 → batplot-1.8.52}/tests/test_undo_deep_all_modes.py +0 -0
  316. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xrd_crosshair_bragg.py +0 -0
  317. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_axis_units.py +0 -0
  318. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_cif_add.py +0 -0
  319. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_full_data_retention.py +0 -0
  320. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_offset_menu.py +0 -0
  321. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_roundtrip.py +0 -0
  322. {batplot-1.8.51 → batplot-1.8.52}/tests/test_xy_source_files.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.51
3
+ Version: 1.8.52
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
@@ -7,7 +7,7 @@ import importlib.abc
7
7
  import importlib.machinery
8
8
  import sys
9
9
 
10
- __version__ = "1.8.51"
10
+ __version__ = "1.8.52"
11
11
 
12
12
 
13
13
  _LEGACY_MODULE_ALIASES = {
@@ -186,7 +186,7 @@ def save_xy_session(
186
186
  from .session import dump_session
187
187
 
188
188
  tick_state = default_flat_tick_state()
189
- dump_session(
189
+ ok = dump_session(
190
190
  target,
191
191
  fig=fig,
192
192
  ax=ax,
@@ -207,7 +207,8 @@ def save_xy_session(
207
207
  show_cif_titles=show_cif_titles,
208
208
  skip_confirm=True,
209
209
  )
210
- fig._last_session_save_path = os.path.abspath(target)
210
+ if not ok:
211
+ raise RuntimeError(f"Failed to save XY session to {target}")
211
212
 
212
213
 
213
214
  @contextmanager
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.52] - 2026-08-08
4
+ - Bug fixes
5
+
6
+
3
7
  ## [1.8.51] - 2026-08-06
4
8
  - Bug fixes
5
9
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.51",
2
+ "version": "1.8.52",
3
3
  "custom_message": "- Bug fixes",
4
4
  "update_notes": [
5
5
  "- Bug fixes"
@@ -149,7 +149,7 @@ def _prompt_one_session_path(
149
149
  print("Invalid number.")
150
150
  return None
151
151
  else:
152
- fname = choice
152
+ fname = os.path.expanduser(choice)
153
153
  if not os.path.splitext(fname)[1]:
154
154
  fname += ".pkl"
155
155
  if os.path.isabs(fname):
@@ -235,8 +235,14 @@ def _save_sessions_as_new(
235
235
  continue
236
236
  try:
237
237
  save_panel(panels[i], target)
238
+ abspath = os.path.abspath(target)
238
239
  fig = _panel_fig(panels[i])
239
- fig._last_session_save_path = os.path.abspath(target) # type: ignore[attr-defined]
240
+ fig._last_session_save_path = abspath # type: ignore[attr-defined]
241
+ # Keep panel.path aligned so next "save to original" uses the new file.
242
+ try:
243
+ panels[i].path = abspath
244
+ except Exception:
245
+ pass
240
246
  print(f"Saved [{i + 1}] to {target}")
241
247
  saved += 1
242
248
  except Exception as exc:
@@ -296,13 +302,17 @@ def run_batch_import_style(
296
302
  if indices is None:
297
303
  print("Import canceled.")
298
304
  return None
299
- path = safe_input(colorize_prompt(path_prompt), cancel_on_interrupt=True).strip()
305
+ path = os.path.expanduser(
306
+ safe_input(colorize_prompt(path_prompt), cancel_on_interrupt=True).strip()
307
+ )
300
308
  if not path or path.lower() == "q":
301
309
  print("Import canceled.")
302
310
  return None
303
311
  payload = load_style(path)
304
312
  if payload is None:
305
313
  return None
314
+ # Capture pre-import snaps for selected panels, then drop frames for any
315
+ # panel that rejects/fails so ``b`` never restores unchanged peers.
306
316
  if prepare:
307
317
  prepare(indices)
308
318
  succeeded: list[int] = []
@@ -316,9 +326,19 @@ def run_batch_import_style(
316
326
  succeeded.append(i)
317
327
  except Exception as exc:
318
328
  print(f"Import failed for [{i + 1}]: {exc}")
319
- return None
329
+ # This index + any not-yet-processed peers need prepare frames dropped.
330
+ skipped.append(i)
331
+ skipped.extend(j for j in indices if j not in succeeded and j not in skipped)
332
+ break
333
+ if skipped and prepare is not None:
334
+ pop_skipped = getattr(prepare, "pop_skipped", None)
335
+ if callable(pop_skipped):
336
+ try:
337
+ pop_skipped(skipped)
338
+ except Exception:
339
+ pass
320
340
  if skipped:
321
- names = ", ".join(str(i + 1) for i in skipped)
341
+ names = ", ".join(str(i + 1) for i in sorted(set(skipped)))
322
342
  print(f"Skipped plot(s) {names} (incompatible or rejected).")
323
343
  if not succeeded:
324
344
  print("Style import did not apply to any panels.")
@@ -351,7 +371,9 @@ def run_batch_export_style(
351
371
 
352
372
  if len(indices) == 1:
353
373
  i = indices[0]
354
- out = safe_input(colorize_prompt(path_prompt_single), cancel_on_interrupt=True).strip()
374
+ out = os.path.expanduser(
375
+ safe_input(colorize_prompt(path_prompt_single), cancel_on_interrupt=True).strip()
376
+ )
355
377
  if not out or out.lower() == "q":
356
378
  print("Export canceled.")
357
379
  return
@@ -107,11 +107,23 @@ def _apply_ec_import(panel, cfg: dict) -> bool | None:
107
107
  return menu_ec._apply_cfg(panel, cfg)
108
108
 
109
109
 
110
+ def _strip_figure_geometry_keys(cfg: dict) -> None:
111
+ """Remove canvas/frame hitchhikers from a style-only (``ps``) payload."""
112
+ fig_block = cfg.get("figure")
113
+ if isinstance(fig_block, dict):
114
+ for key in ("canvas_size", "frame_size", "axes_fraction", "size"):
115
+ fig_block.pop(key, None)
116
+ if not fig_block:
117
+ cfg.pop("figure", None)
118
+
119
+
110
120
  def _export_ec_style(panel, path: str, sub: str) -> None:
111
121
  cfg = _capture_ec(panel)
112
122
  cfg["kind"] = "ec_style_geom" if sub == "psg" else "ec_style"
113
123
  if sub == "ps":
114
124
  cfg.pop("geometry", None)
125
+ cfg.pop("xaxis_dual", None)
126
+ _strip_figure_geometry_keys(cfg)
115
127
  with open(path, "w", encoding="utf-8") as fh:
116
128
  json.dump(cfg, fh, indent=2)
117
129
 
@@ -139,6 +151,7 @@ def _export_cpc_style(panel, path: str, sub: str) -> None:
139
151
  cfg["kind"] = "cpc_style_geom" if sub == "psg" else "cpc_style"
140
152
  if sub == "ps":
141
153
  cfg.pop("geometry", None)
154
+ _strip_figure_geometry_keys(cfg)
142
155
  with open(path, "w", encoding="utf-8") as fh:
143
156
  json.dump(cfg, fh, indent=2)
144
157
 
@@ -62,7 +62,7 @@ def merge_spines_props(
62
62
  *,
63
63
  mode: str,
64
64
  ) -> dict:
65
- """mode: ``linewidth`` | ``color`` | ``all``."""
65
+ """mode: ``linewidth`` | ``color`` | ``visible`` | ``all``."""
66
66
  out = copy.deepcopy(peer) if isinstance(peer, dict) else {}
67
67
  if not isinstance(ref, dict):
68
68
  return out
@@ -73,8 +73,9 @@ def merge_spines_props(
73
73
  if mode in ("linewidth", "all"):
74
74
  if "linewidth" in props:
75
75
  dest["linewidth"] = props["linewidth"]
76
- if "visible" in props:
77
- dest["visible"] = props["visible"]
76
+ # Visibility is owned by WASD (``t``).
77
+ if mode in ("visible", "all") and "visible" in props:
78
+ dest["visible"] = props["visible"]
78
79
  if mode in ("color", "all"):
79
80
  if "color" in props:
80
81
  dest["color"] = props["color"]
@@ -30,6 +30,12 @@ class SyncUndoStacks:
30
30
  if len(self._stacks[i]) > 40:
31
31
  self._stacks[i].pop(0)
32
32
 
33
+ def pop_indices(self, indices: List[int]) -> None:
34
+ """Drop the latest frame for each index (failed import cleanup)."""
35
+ for i in indices:
36
+ if 0 <= i < len(self._stacks) and len(self._stacks[i]) > 1:
37
+ self._stacks[i].pop()
38
+
33
39
  def can_undo(self) -> bool:
34
40
  """True when at least one panel has more than the baseline snapshot (index 0)."""
35
41
  return any(len(s) > 1 for s in self._stacks)
@@ -47,6 +53,9 @@ class SyncUndoStacks:
47
53
  restore_fn(i, snap)
48
54
  restored += 1
49
55
  except Exception as exc:
56
+ # Keep panel stacks aligned: a failed restore must not drop the
57
+ # only copy of that undo level (otherwise later ``b`` desyncs).
58
+ stack.append(snap)
50
59
  print(f"Undo failed for panel {i + 1}: {exc}")
51
60
  if restored:
52
61
  print(f"Undo: restored {restored} panel(s).")
@@ -55,6 +64,42 @@ class SyncUndoStacks:
55
64
  return False
56
65
 
57
66
 
67
+ def make_style_import_prepare(
68
+ undo: SyncUndoStacks,
69
+ panels: Sequence[Any],
70
+ capture_panel: Callable[[Any], Any],
71
+ restore_panel: Callable[[Any, Any], None] | None = None,
72
+ ) -> Callable[[List[int]], None]:
73
+ """Build a ``prepare`` callback that can roll back skipped import frames.
74
+
75
+ When ``restore_panel`` is provided, skipped/failed panels are fully restored
76
+ from the pre-import tip before the tip is dropped (discard-only would leave
77
+ a half-applied panel with no ``b`` recovery).
78
+ """
79
+
80
+ def prepare(indices: List[int]) -> None:
81
+ undo.push_indices(indices, [capture_panel(panels[i]) for i in indices])
82
+
83
+ def pop_skipped(skipped: List[int]) -> None:
84
+ for i in skipped:
85
+ if not (0 <= i < len(undo._stacks)):
86
+ continue
87
+ stack = undo._stacks[i]
88
+ if len(stack) <= 1:
89
+ continue
90
+ snap = stack.pop()
91
+ if restore_panel is None:
92
+ continue
93
+ try:
94
+ restore_panel(panels[i], snap)
95
+ except Exception as exc:
96
+ stack.append(snap)
97
+ print(f"Import rollback failed for panel {i + 1}: {exc}")
98
+
99
+ prepare.pop_skipped = pop_skipped # type: ignore[attr-defined]
100
+ return prepare
101
+
102
+
58
103
  def set_all_panel_figure_titles(panels: Sequence[Any]) -> None:
59
104
  for panel in panels:
60
105
  set_panel_figure_title(panel)
@@ -23,6 +23,7 @@ from ..common.spines import (
23
23
  apply_wasd_tick_params,
24
24
  build_wasd_state,
25
25
  run_spine_tick_menu,
26
+ set_primary_axis_title,
26
27
  sync_tick_state_from_wasd,
27
28
  )
28
29
  from ..common.terminal import colorize_inline_commands, colorize_prompt, safe_input
@@ -67,6 +68,9 @@ _CPC_COLOR_MULTI_FIELDS = (
67
68
  "efficiency_hollow",
68
69
  )
69
70
 
71
+ # Series color sync fields (batch ``c``) — include alpha for p/i/s/b parity.
72
+ _CPC_SERIES_COLOR_FIELDS = ("color", "hollow", "alpha")
73
+
70
74
 
71
75
  def _peer_cpc_style(panel: CpcPanel) -> dict:
72
76
  return _style_snapshot(
@@ -191,12 +195,18 @@ def apply_cpc_labels_only(panel: CpcPanel, cfg: dict, *, silent: bool = True) ->
191
195
 
192
196
 
193
197
  def apply_cpc_wasd_chrome_only(panel: CpcPanel, cfg: dict, *, silent: bool = True) -> bool:
194
- """Batch ``t``: WASD / ticks / spine widths (not series colors)."""
198
+ """Batch ``t``: WASD / ticks / title offsets (not ``l`` widths or series colors)."""
199
+ cfg = dict(cfg) if isinstance(cfg, dict) else {}
200
+ ticks = cfg.get("ticks")
201
+ if isinstance(ticks, dict):
202
+ ticks = dict(ticks)
203
+ ticks.pop("widths", None)
204
+ cfg["ticks"] = ticks
195
205
  return apply_cpc_scoped_sync(
196
206
  panel,
197
207
  cfg,
198
208
  top_keys=frozenset({"wasd_state", "ticks", "spines", "title_offsets", "labelpads"}),
199
- spine_mode="linewidth",
209
+ spine_mode="visible",
200
210
  silent=silent,
201
211
  )
202
212
 
@@ -240,7 +250,7 @@ def apply_cpc_colors_only(panel: CpcPanel, cfg: dict, *, silent: bool = True) ->
240
250
  top_keys=frozenset({"spine_colors", "spine_colors_auto", "spines"}),
241
251
  spine_mode="color",
242
252
  multi_file_fields=_CPC_COLOR_MULTI_FIELDS,
243
- series_color_fields=("color", "hollow"),
253
+ series_color_fields=_CPC_SERIES_COLOR_FIELDS,
244
254
  silent=silent,
245
255
  )
246
256
 
@@ -366,15 +376,10 @@ def cpc_run_file_visibility_menu(
366
376
  push_state("visibility")
367
377
  for idx in indices_to_toggle:
368
378
  f = file_data[idx]
369
- new_vis = not f.get("visible", True)
370
- f["visible"] = new_vis
371
- for key in ("sc_charge", "sc_discharge", "sc_eff"):
372
- sc = f.get(key)
373
- if sc is not None:
374
- try:
375
- sc.set_visible(new_vis)
376
- except Exception:
377
- pass
379
+ f["visible"] = not f.get("visible", True)
380
+ from ..cpc.panel_menus import apply_cpc_file_artist_visibility
381
+
382
+ apply_cpc_file_artist_visibility(fig, file_data)
378
383
  try:
379
384
  rebuild_legend(ax, ax2, file_data, preserve_position=True)
380
385
  fig.canvas.draw_idle()
@@ -384,7 +389,14 @@ def cpc_run_file_visibility_menu(
384
389
  print(f"Toggled: {', '.join(names)}")
385
390
 
386
391
 
387
- def cpc_set_spine_color(fig: Any, ax: Any, ax2: Any, spine_name: str, color) -> None:
392
+ def cpc_set_spine_color(
393
+ fig: Any,
394
+ ax: Any,
395
+ ax2: Any,
396
+ spine_name: str,
397
+ color,
398
+ tick_state=None,
399
+ ) -> None:
388
400
  """Set one spine's color (with matching ticks/labels), mirroring the
389
401
  ``_set_spine_color`` closure ``_apply_style`` builds for normal mode."""
390
402
  if not hasattr(fig, "_cpc_spine_colors") or not isinstance(getattr(fig, "_cpc_spine_colors", None), dict):
@@ -399,11 +411,16 @@ def cpc_set_spine_color(fig: Any, ax: Any, ax2: Any, spine_name: str, color) ->
399
411
  "left": [ax],
400
412
  "right": [ax2],
401
413
  }
414
+ ts = tick_state
415
+ if ts is None:
416
+ ts = getattr(ax, "_saved_tick_state", None)
402
417
  for curr_ax in axes_map.get(spine_name, [ax, ax2]):
403
418
  if curr_ax is None or spine_name not in curr_ax.spines:
404
419
  continue
405
420
  try:
406
- _ui_set_spine_side_color(curr_ax, spine_name, color, fig=fig)
421
+ _ui_set_spine_side_color(
422
+ curr_ax, spine_name, color, fig=fig, tick_state=ts
423
+ )
407
424
  except Exception:
408
425
  pass
409
426
 
@@ -444,21 +461,24 @@ def run_cpc_batch_spine_menu(
444
461
  "ticks": bool(tick_state.get("b_ticks", tick_state.get("bx", True))),
445
462
  "minor": bool(tick_state.get("mbx", False)),
446
463
  "labels": bool(tick_state.get("b_labels", tick_state.get("bx", True))),
447
- "title": bool(ax.get_xlabel()),
464
+ "title": bool(ax.xaxis.label.get_visible()),
448
465
  },
449
466
  "left": {
450
467
  "spine": _spine_visible("left", ax),
451
468
  "ticks": bool(tick_state.get("l_ticks", tick_state.get("ly", True))),
452
469
  "minor": bool(tick_state.get("mly", False)),
453
470
  "labels": bool(tick_state.get("l_labels", tick_state.get("ly", True))),
454
- "title": bool(ax.get_ylabel()),
471
+ "title": bool(ax.yaxis.label.get_visible()),
455
472
  },
456
473
  "right": {
457
474
  "spine": _spine_visible("right", ax2),
458
475
  "ticks": bool(tick_state.get("r_ticks", tick_state.get("ry", True))),
459
476
  "minor": bool(tick_state.get("mry", False)),
460
477
  "labels": bool(tick_state.get("r_labels", tick_state.get("ry", True))),
461
- "title": bool(ax2.yaxis.get_label().get_text()) and bool(sc_eff.get_visible() if sc_eff is not None else False),
478
+ "title": (
479
+ bool(ax2.yaxis.label.get_visible())
480
+ and bool(sc_eff.get_visible() if sc_eff is not None else False)
481
+ ),
462
482
  },
463
483
  }
464
484
  fig._cpc_wasd_state = wasd
@@ -473,30 +493,24 @@ def run_cpc_batch_spine_menu(
473
493
  apply_wasd_tick_params(ax2, wasd, x_sides=(), y_sides=("right",), y_mode="right")
474
494
 
475
495
  try:
476
- if bool(wasd["bottom"]["title"]):
477
- if hasattr(ax, "_stored_xlabel") and isinstance(ax._stored_xlabel, str) and ax._stored_xlabel:
478
- ax.set_xlabel(ax._stored_xlabel)
479
- else:
480
- if not hasattr(ax, "_stored_xlabel"):
481
- try:
482
- ax._stored_xlabel = ax.get_xlabel()
483
- except Exception:
484
- ax._stored_xlabel = ""
485
- ax.set_xlabel("")
496
+ set_primary_axis_title(
497
+ ax, "x", on=bool(wasd["bottom"]["title"]), stored_attr="_stored_xlabel"
498
+ )
486
499
  except Exception:
487
500
  pass
488
501
 
489
502
  try:
490
- if not hasattr(ax, "_stored_top_xlabel") or not ax._stored_top_xlabel:
503
+ if not hasattr(ax, "_stored_top_xlabel") or not isinstance(ax._stored_top_xlabel, str):
491
504
  current_xlabel = ax.get_xlabel()
492
505
  if current_xlabel:
493
506
  ax._stored_top_xlabel = current_xlabel
494
- elif hasattr(ax, "_stored_xlabel") and ax._stored_xlabel:
507
+ elif hasattr(ax, "_stored_xlabel") and isinstance(ax._stored_xlabel, str) and ax._stored_xlabel:
495
508
  ax._stored_top_xlabel = ax._stored_xlabel
496
509
  else:
497
510
  ax._stored_top_xlabel = ""
498
511
 
499
- if bool(wasd["top"]["title"]) and ax._stored_top_xlabel:
512
+ ax._top_xlabel_on = bool(wasd["top"]["title"])
513
+ if bool(wasd["top"]["title"]) and isinstance(getattr(ax, "_stored_top_xlabel", None), str):
500
514
  if not hasattr(ax, "_top_xlabel_text") or ax._top_xlabel_text is None:
501
515
  ax._top_xlabel_text = ax.text(
502
516
  0.5, 1.0, "", transform=ax.transAxes,
@@ -544,31 +558,20 @@ def run_cpc_batch_spine_menu(
544
558
  pass
545
559
 
546
560
  try:
547
- if bool(wasd["left"]["title"]):
548
- if hasattr(ax, "_stored_ylabel") and isinstance(ax._stored_ylabel, str) and ax._stored_ylabel:
549
- ax.set_ylabel(ax._stored_ylabel)
550
- else:
551
- if not hasattr(ax, "_stored_ylabel"):
552
- try:
553
- ax._stored_ylabel = ax.get_ylabel()
554
- except Exception:
555
- ax._stored_ylabel = ""
556
- ax.set_ylabel("")
561
+ set_primary_axis_title(
562
+ ax, "y", on=bool(wasd["left"]["title"]), stored_attr="_stored_ylabel"
563
+ )
557
564
  except Exception:
558
565
  pass
559
566
 
560
567
  try:
561
568
  eff_visible = bool(sc_eff.get_visible()) if sc_eff is not None else False
562
- if bool(wasd["right"]["title"]) and eff_visible:
563
- if hasattr(ax2, "_stored_ylabel") and isinstance(ax2._stored_ylabel, str) and ax2._stored_ylabel:
564
- ax2.set_ylabel(ax2._stored_ylabel)
565
- else:
566
- if not hasattr(ax2, "_stored_ylabel"):
567
- try:
568
- ax2._stored_ylabel = ax2.get_ylabel()
569
- except Exception:
570
- ax2._stored_ylabel = ""
571
- ax2.set_ylabel("")
569
+ set_primary_axis_title(
570
+ ax2,
571
+ "y",
572
+ on=bool(wasd["right"]["title"]) and eff_visible,
573
+ stored_attr="_stored_ylabel",
574
+ )
572
575
  except Exception:
573
576
  pass
574
577
 
@@ -612,6 +615,20 @@ def run_cpc_batch_spine_menu(
612
615
  )
613
616
  draw_all()
614
617
 
618
+ def _title_offsets() -> None:
619
+ from ..common.title_offsets import run_title_offset_nudge_menu
620
+
621
+ # Right efficiency title lives on twin ``ax2``, not the left axes.
622
+ run_title_offset_nudge_menu(
623
+ fig=fig,
624
+ ax=ax,
625
+ push_state=lambda: None,
626
+ safe_input=safe_input,
627
+ colorize_prompt=colorize_prompt,
628
+ draw=_draw_spine_menu,
629
+ axis_by_side={"d": ax2},
630
+ )
631
+
615
632
  def _edit() -> None:
616
633
  run_spine_tick_menu(
617
634
  fig=fig,
@@ -628,6 +645,7 @@ def run_cpc_batch_spine_menu(
628
645
  axis_map={"x": ax.xaxis, "y": ax.yaxis, "r": ax2.yaxis},
629
646
  direction_axes=[ax, ax2],
630
647
  length_axes=[ax, ax2],
648
+ title_offset_handler=_title_offsets,
631
649
  on_quit=lambda: setattr(ax, "_saved_tick_state", dict(tick_state)),
632
650
  )
633
651