batplot 1.8.46__tar.gz → 1.8.48__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.
- {batplot-1.8.46 → batplot-1.8.48}/PKG-INFO +1 -5
- {batplot-1.8.46 → batplot-1.8.48}/README.md +0 -4
- {batplot-1.8.46 → batplot-1.8.48}/batplot/__init__.py +1 -1
- {batplot-1.8.46 → batplot-1.8.48}/batplot/_mpl_backend.py +10 -7
- {batplot-1.8.46 → batplot-1.8.48}/batplot/batch.py +22 -387
- {batplot-1.8.46 → batplot-1.8.48}/batplot/batplot.py +0 -1
- {batplot-1.8.46 → batplot-1.8.48}/batplot/color_utils.py +14 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/data/CHANGELOG.md +13 -0
- batplot-1.8.48/batplot/data/latest_release_notes.json +7 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/dev_upgrade.py +144 -96
- {batplot-1.8.46 → batplot-1.8.48}/batplot/ec_common.py +51 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/batch_commands.py +11 -18
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_crosshair.py +165 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_figure_io.py +261 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_geom_helpers.py +229 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_io.py +389 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_menu_helpers.py +339 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_menu_io.py +103 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/batch_panel_state.py +304 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/common.py +64 -28
- batplot-1.8.48/batplot/plot_modes/batch_session/cpc_batch_helpers.py +418 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/ec_batch_helpers.py +457 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/histo_batch_helpers.py +228 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/kinds.py +3 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/load.py +6 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/menu_cpc.py +730 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/menu_ec.py +680 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/menu_histo.py +511 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/menu_operando.py +750 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/menu_xy.py +568 -0
- batplot-1.8.48/batplot/plot_modes/batch_session/operando_batch_helpers.py +472 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/routing.py +7 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/xy_batch_helpers.py +106 -4
- batplot-1.8.48/batplot/plot_modes/common/batch_font.py +137 -0
- batplot-1.8.48/batplot/plot_modes/common/crosshair_export.py +116 -0
- batplot-1.8.48/batplot/plot_modes/common/font_extras.py +380 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/fonts.py +102 -2
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/menu_rendering.py +7 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/menus.py +248 -36
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/palettes.py +6 -1
- batplot-1.8.48/batplot/plot_modes/common/session_helpers.py +251 -0
- batplot-1.8.48/batplot/plot_modes/common/session_key_smoke.py +510 -0
- batplot-1.8.48/batplot/plot_modes/common/size_spec.py +72 -0
- batplot-1.8.48/batplot/plot_modes/common/sources.py +184 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/spines.py +44 -37
- batplot-1.8.48/batplot/plot_modes/common/state_capture.py +98 -0
- batplot-1.8.48/batplot/plot_modes/common/style_routing.py +128 -0
- batplot-1.8.48/batplot/plot_modes/common/terminal.py +294 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/__init__.py +2 -2
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/actions.py +12 -5
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/colors.py +111 -39
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/interactive.py +244 -1344
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/labels.py +68 -64
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/legend.py +25 -6
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/routing.py +13 -4
- batplot-1.8.48/batplot/plot_modes/cpc/session.py +1006 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/snapshots.py +13 -7
- batplot-1.8.48/batplot/plot_modes/cpc/style.py +1274 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/__init__.py +2 -2
- batplot-1.8.48/batplot/plot_modes/electrochem/actions.py +673 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/colors.py +134 -4
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/dqdv_2d.py +43 -34
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/export.py +3 -1
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/interactive.py +326 -209
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/labels.py +61 -47
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/legend.py +3 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/line_style.py +53 -46
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/routing.py +29 -10
- batplot-1.8.48/batplot/plot_modes/electrochem/session.py +1207 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/spine_colors.py +9 -9
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/style.py +184 -138
- batplot-1.8.48/batplot/plot_modes/electrochem/style_apply.py +795 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/actions.py +23 -7
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/colors.py +72 -9
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/density_curve.py +57 -32
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/fonts.py +64 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/interactive.py +181 -62
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/labels.py +29 -27
- batplot-1.8.48/batplot/plot_modes/histo/line_style.py +176 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/plot.py +187 -22
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/routing.py +2 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/session.py +32 -2
- batplot-1.8.48/batplot/plot_modes/histo/spines.py +581 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/toggles.py +24 -16
- batplot-1.8.48/batplot/plot_modes/histo/y_range.py +112 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/__init__.py +2 -2
- batplot-1.8.48/batplot/plot_modes/operando/actions.py +872 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/grid.py +27 -18
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/interactive.py +492 -465
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/labels.py +66 -58
- batplot-1.8.48/batplot/plot_modes/operando/layout_menu.py +494 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/line_style.py +42 -39
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/menu.py +0 -5
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/peaks.py +94 -95
- batplot-1.8.48/batplot/plot_modes/operando/session.py +1056 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/style.py +40 -23
- batplot-1.8.48/batplot/plot_modes/operando/style_apply.py +970 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/visibility.py +93 -77
- batplot-1.8.48/batplot/plot_modes/session_routing.py +341 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/__init__.py +2 -2
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/actions.py +10 -3
- batplot-1.8.48/batplot/plot_modes/xy/axis_range.py +568 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/cif.py +11 -10
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/colors.py +25 -13
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/derivative.py +60 -1
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/interactive.py +101 -155
- batplot-1.8.48/batplot/plot_modes/xy/labels.py +160 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/line_style.py +80 -58
- batplot-1.8.48/batplot/plot_modes/xy/session.py +1336 -0
- batplot-1.8.48/batplot/plot_modes/xy/spines.py +258 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/style.py +195 -44
- {batplot-1.8.46 → batplot-1.8.48}/batplot/readers.py +9 -374
- batplot-1.8.48/batplot/readers_xrd.py +401 -0
- batplot-1.8.48/batplot/session.py +107 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/ui.py +855 -164
- {batplot-1.8.46 → batplot-1.8.48}/batplot/version_check.py +2 -2
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/PKG-INFO +1 -5
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/SOURCES.txt +53 -1
- {batplot-1.8.46 → batplot-1.8.48}/pyproject.toml +1 -1
- batplot-1.8.48/tests/test_batch_all_modes_sync.py +134 -0
- batplot-1.8.48/tests/test_batch_crosshair.py +68 -0
- batplot-1.8.48/tests/test_batch_ec_cycles_status.py +42 -0
- batplot-1.8.48/tests/test_batch_font_menu.py +108 -0
- batplot-1.8.48/tests/test_batch_geom_menu.py +38 -0
- batplot-1.8.48/tests/test_batch_menu_parity.py +442 -0
- batplot-1.8.48/tests/test_batch_panel_status.py +77 -0
- batplot-1.8.48/tests/test_batch_pisb_contract.py +183 -0
- batplot-1.8.48/tests/test_batch_session.py +674 -0
- batplot-1.8.48/tests/test_batch_spine_sync.py +73 -0
- batplot-1.8.48/tests/test_cli_all_flags.py +154 -0
- batplot-1.8.48/tests/test_color_listing_visible.py +138 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_common_palettes.py +20 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_contracts.py +16 -12
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_cpc_roundtrip.py +1 -0
- batplot-1.8.48/tests/test_critical_stability_fixes.py +90 -0
- batplot-1.8.48/tests/test_cross_platform_parity.py +81 -0
- batplot-1.8.48/tests/test_crosshair_export.py +49 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_dev_upgrade.py +32 -0
- batplot-1.8.48/tests/test_dqdv_contour_session_keys.py +106 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_ec_roundtrip.py +236 -1
- batplot-1.8.48/tests/test_histo.py +1514 -0
- batplot-1.8.48/tests/test_histo_interactive_imports.py +64 -0
- batplot-1.8.48/tests/test_imk_stderr_guard.py +22 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_interactive_menu_smoke.py +22 -4
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_interactive_state.py +172 -3
- batplot-1.8.48/tests/test_menu_handler_imports.py +202 -0
- batplot-1.8.48/tests/test_menu_prompt_order.py +58 -0
- batplot-1.8.48/tests/test_operando_batch_menu.py +143 -0
- batplot-1.8.48/tests/test_operando_layout_menu.py +75 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_operando_roundtrip.py +8 -4
- batplot-1.8.48/tests/test_session_backward_compat.py +81 -0
- batplot-1.8.48/tests/test_session_font_restore.py +155 -0
- batplot-1.8.48/tests/test_state_capture.py +55 -0
- batplot-1.8.48/tests/test_style_routing.py +115 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_xy_modules.py +115 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_xy_roundtrip.py +50 -0
- batplot-1.8.48/tests/test_xy_source_files.py +46 -0
- batplot-1.8.46/batplot/data/latest_release_notes.json +0 -7
- batplot-1.8.46/batplot/plot_modes/batch_session/batch_io.py +0 -178
- batplot-1.8.46/batplot/plot_modes/batch_session/menu_cpc.py +0 -291
- batplot-1.8.46/batplot/plot_modes/batch_session/menu_ec.py +0 -318
- batplot-1.8.46/batplot/plot_modes/batch_session/menu_histo.py +0 -407
- batplot-1.8.46/batplot/plot_modes/batch_session/menu_operando.py +0 -292
- batplot-1.8.46/batplot/plot_modes/batch_session/menu_xy.py +0 -429
- batplot-1.8.46/batplot/plot_modes/common/sources.py +0 -68
- batplot-1.8.46/batplot/plot_modes/common/terminal.py +0 -128
- batplot-1.8.46/batplot/plot_modes/cpc/session.py +0 -15
- batplot-1.8.46/batplot/plot_modes/electrochem/actions.py +0 -1256
- batplot-1.8.46/batplot/plot_modes/electrochem/session.py +0 -15
- batplot-1.8.46/batplot/plot_modes/histo/spines.py +0 -291
- batplot-1.8.46/batplot/plot_modes/operando/actions.py +0 -1571
- batplot-1.8.46/batplot/plot_modes/operando/session.py +0 -15
- batplot-1.8.46/batplot/plot_modes/session_routing.py +0 -1086
- batplot-1.8.46/batplot/plot_modes/xy/axis_range.py +0 -624
- batplot-1.8.46/batplot/plot_modes/xy/labels.py +0 -160
- batplot-1.8.46/batplot/plot_modes/xy/session.py +0 -15
- batplot-1.8.46/batplot/session.py +0 -4691
- batplot-1.8.46/tests/test_batch_session.py +0 -241
- batplot-1.8.46/tests/test_histo.py +0 -598
- {batplot-1.8.46 → batplot-1.8.48}/DEVELOPING.md +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/LICENSE +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/MANIFEST.in +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/NOTICE +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/args.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/canvas_interactive.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/cif.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/cli.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/cli_save.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/config.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/converters.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/modes.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/__init__.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/batch_session/__init__.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/__init__.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/axis_state.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/files.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/interactive_state.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/smoothing.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/common/title_offsets.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/cpc/menu.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/legend_order.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/electrochem/menu.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/__init__.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/load.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/histo/wizard.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/colors.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/ions_axis.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/layout.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/plot.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/operando/routing.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/arrange.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/data_ops.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/game.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/menu.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/peaks.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/pipeline.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plot_modes/xy/smoothing.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/plotting.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/showcol.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/style.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot/utils.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/batplot.egg-info/top_level.txt +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/setup.cfg +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/setup.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_bruker_intensity.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_cli_save.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_cli_smoke.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_color_utils.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_common_files.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_csv_readers.py +0 -0
- {batplot-1.8.46 → batplot-1.8.48}/tests/test_mpl_backend.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.48
|
|
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
|
|
@@ -448,10 +448,6 @@ batplot --v # Version and release notes
|
|
|
448
448
|
batplot --m # Open illustrated manual
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
-
- [USER_MANUAL.md](USER_MANUAL.md) — Detailed usage and workflows
|
|
452
|
-
- [FLAGS_REFERENCE.md](FLAGS_REFERENCE.md) — Complete flag reference by mode
|
|
453
|
-
- [DEVELOPING.md](DEVELOPING.md) — Developer guide for extending Batplot
|
|
454
|
-
|
|
455
451
|
---
|
|
456
452
|
|
|
457
453
|
## Requirements
|
|
@@ -394,10 +394,6 @@ batplot --v # Version and release notes
|
|
|
394
394
|
batplot --m # Open illustrated manual
|
|
395
395
|
```
|
|
396
396
|
|
|
397
|
-
- [USER_MANUAL.md](USER_MANUAL.md) — Detailed usage and workflows
|
|
398
|
-
- [FLAGS_REFERENCE.md](FLAGS_REFERENCE.md) — Complete flag reference by mode
|
|
399
|
-
- [DEVELOPING.md](DEVELOPING.md) — Developer guide for extending Batplot
|
|
400
|
-
|
|
401
397
|
---
|
|
402
398
|
|
|
403
399
|
## Requirements
|
|
@@ -252,20 +252,23 @@ def prime_interactive_figure(fig=None) -> None:
|
|
|
252
252
|
"""Prepare a matplotlib window before an interactive menu (non-blocking)."""
|
|
253
253
|
import matplotlib.pyplot as plt
|
|
254
254
|
|
|
255
|
+
from .plot_modes.common.terminal import imk_stderr_guard
|
|
256
|
+
|
|
255
257
|
try:
|
|
256
258
|
plt.ion()
|
|
257
259
|
except Exception:
|
|
258
260
|
pass
|
|
259
|
-
|
|
261
|
+
with imk_stderr_guard():
|
|
262
|
+
if fig is not None:
|
|
263
|
+
try:
|
|
264
|
+
fig.canvas.draw_idle()
|
|
265
|
+
fig.canvas.flush_events()
|
|
266
|
+
except Exception:
|
|
267
|
+
pass
|
|
260
268
|
try:
|
|
261
|
-
|
|
262
|
-
fig.canvas.flush_events()
|
|
269
|
+
plt.show(block=False)
|
|
263
270
|
except Exception:
|
|
264
271
|
pass
|
|
265
|
-
try:
|
|
266
|
-
plt.show(block=False)
|
|
267
|
-
except Exception:
|
|
268
|
-
pass
|
|
269
272
|
|
|
270
273
|
|
|
271
274
|
def hold_figure_open() -> None:
|
|
@@ -10,7 +10,9 @@ import matplotlib.pyplot as plt # type: ignore[import]
|
|
|
10
10
|
import numpy as np # type: ignore[import]
|
|
11
11
|
|
|
12
12
|
from .color_utils import get_colormap
|
|
13
|
+
from .plot_modes.common.fonts import sync_legend_title_fontsize
|
|
13
14
|
|
|
15
|
+
from .ec_common import _resolve_mass
|
|
14
16
|
from .readers import (
|
|
15
17
|
read_gr_file,
|
|
16
18
|
read_xrd_vendor_file,
|
|
@@ -37,19 +39,7 @@ def _is_mpt_like_ext(ext: str) -> bool:
|
|
|
37
39
|
return (ext or '').lower() in _MPT_LIKE_EXTS
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
"""Return mass (mg) for file at file_idx from a --mass list or single value."""
|
|
42
|
-
if mass_arg is None:
|
|
43
|
-
return None
|
|
44
|
-
if isinstance(mass_arg, (int, float)):
|
|
45
|
-
return float(mass_arg)
|
|
46
|
-
if isinstance(mass_arg, list):
|
|
47
|
-
if len(mass_arg) == 1:
|
|
48
|
-
return float(mass_arg[0])
|
|
49
|
-
if file_idx < len(mass_arg):
|
|
50
|
-
return float(mass_arg[file_idx])
|
|
51
|
-
return float(mass_arg[-1])
|
|
52
|
-
return None
|
|
42
|
+
from .ec_common import _resolve_mass
|
|
53
43
|
from .utils import _confirm_overwrite, natural_sort_key, ensure_subdirectory
|
|
54
44
|
|
|
55
45
|
|
|
@@ -72,364 +62,24 @@ def _load_style_file(style_path: str) -> dict | None:
|
|
|
72
62
|
|
|
73
63
|
|
|
74
64
|
def _apply_xy_style(fig, ax, cfg: dict):
|
|
75
|
-
"""Apply style
|
|
76
|
-
|
|
77
|
-
Applies formatting from .bps/.bpsg files including fonts, colors,
|
|
78
|
-
tick parameters, and geometry (if present in .bpsg files).
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
fig: Matplotlib figure object
|
|
82
|
-
ax: Matplotlib axes object
|
|
83
|
-
cfg: Style configuration dictionary
|
|
84
|
-
"""
|
|
85
|
-
try:
|
|
86
|
-
# Apply fonts
|
|
87
|
-
font_cfg = cfg.get('font', {})
|
|
88
|
-
if font_cfg:
|
|
89
|
-
family = font_cfg.get('family')
|
|
90
|
-
size = font_cfg.get('size')
|
|
91
|
-
if family:
|
|
92
|
-
plt.rcParams['font.sans-serif'] = [family] if isinstance(family, str) else family
|
|
93
|
-
if size is not None:
|
|
94
|
-
plt.rcParams['font.size'] = size
|
|
95
|
-
|
|
96
|
-
# Apply figure size if present
|
|
97
|
-
fig_cfg = cfg.get('figure', {})
|
|
98
|
-
if fig_cfg:
|
|
99
|
-
canvas_size = fig_cfg.get('canvas_size')
|
|
100
|
-
if canvas_size and isinstance(canvas_size, (list, tuple)) and len(canvas_size) == 2:
|
|
101
|
-
try:
|
|
102
|
-
fig.set_size_inches(canvas_size[0], canvas_size[1])
|
|
103
|
-
except Exception:
|
|
104
|
-
pass
|
|
105
|
-
|
|
106
|
-
# Apply tick parameters
|
|
107
|
-
ticks_cfg = cfg.get('ticks', {})
|
|
108
|
-
if ticks_cfg:
|
|
109
|
-
# Tick widths
|
|
110
|
-
widths = ticks_cfg.get('widths', {})
|
|
111
|
-
if widths.get('x_major') is not None:
|
|
112
|
-
ax.tick_params(axis='x', which='major', width=widths['x_major'])
|
|
113
|
-
if widths.get('x_minor') is not None:
|
|
114
|
-
ax.tick_params(axis='x', which='minor', width=widths['x_minor'])
|
|
115
|
-
if widths.get('y_major') is not None:
|
|
116
|
-
ax.tick_params(axis='y', which='major', width=widths['y_major'])
|
|
117
|
-
if widths.get('y_minor') is not None:
|
|
118
|
-
ax.tick_params(axis='y', which='minor', width=widths['y_minor'])
|
|
119
|
-
|
|
120
|
-
# Tick lengths
|
|
121
|
-
lengths = ticks_cfg.get('lengths', {})
|
|
122
|
-
if lengths.get('major') is not None:
|
|
123
|
-
ax.tick_params(axis='both', which='major', length=lengths['major'])
|
|
124
|
-
if lengths.get('minor') is not None:
|
|
125
|
-
ax.tick_params(axis='both', which='minor', length=lengths['minor'])
|
|
126
|
-
|
|
127
|
-
# Tick direction
|
|
128
|
-
direction = ticks_cfg.get('direction')
|
|
129
|
-
if direction:
|
|
130
|
-
ax.tick_params(axis='both', which='both', direction=direction)
|
|
131
|
-
|
|
132
|
-
# Apply geometry if present (for .bpsg files)
|
|
133
|
-
kind = cfg.get('kind', '')
|
|
134
|
-
if 'geom' in kind.lower() and 'geometry' in cfg:
|
|
135
|
-
geom = cfg.get('geometry', {})
|
|
136
|
-
if geom.get('xlabel'):
|
|
137
|
-
ax.set_xlabel(geom['xlabel'])
|
|
138
|
-
if geom.get('ylabel'):
|
|
139
|
-
ax.set_ylabel(geom['ylabel'])
|
|
140
|
-
if 'xlim' in geom and isinstance(geom['xlim'], (list, tuple)) and len(geom['xlim']) == 2:
|
|
141
|
-
try:
|
|
142
|
-
ax.set_xlim(geom['xlim'][0], geom['xlim'][1])
|
|
143
|
-
except Exception:
|
|
144
|
-
pass
|
|
145
|
-
if 'ylim' in geom and isinstance(geom['ylim'], (list, tuple)) and len(geom['ylim']) == 2:
|
|
146
|
-
try:
|
|
147
|
-
ax.set_ylim(geom['ylim'][0], geom['ylim'][1])
|
|
148
|
-
except Exception:
|
|
149
|
-
pass
|
|
150
|
-
|
|
151
|
-
# Apply line colors if available
|
|
152
|
-
lines_cfg = cfg.get('lines', [])
|
|
153
|
-
if lines_cfg and len(ax.lines) > 0:
|
|
154
|
-
for entry in lines_cfg:
|
|
155
|
-
idx = entry.get('index')
|
|
156
|
-
if idx is not None and 0 <= idx < len(ax.lines):
|
|
157
|
-
ln = ax.lines[idx]
|
|
158
|
-
if 'color' in entry:
|
|
159
|
-
try:
|
|
160
|
-
ln.set_color(entry['color'])
|
|
161
|
-
except Exception:
|
|
162
|
-
pass
|
|
163
|
-
if 'linewidth' in entry:
|
|
164
|
-
try:
|
|
165
|
-
ln.set_linewidth(entry['linewidth'])
|
|
166
|
-
except Exception:
|
|
167
|
-
pass
|
|
168
|
-
if 'linestyle' in entry:
|
|
169
|
-
try:
|
|
170
|
-
ln.set_linestyle(entry['linestyle'])
|
|
171
|
-
except Exception:
|
|
172
|
-
pass
|
|
173
|
-
|
|
174
|
-
# Apply spine configuration
|
|
175
|
-
spines_cfg = cfg.get('spines', {})
|
|
176
|
-
for spine_name, spine_props in spines_cfg.items():
|
|
177
|
-
if spine_name in ax.spines:
|
|
178
|
-
sp = ax.spines[spine_name]
|
|
179
|
-
if 'lw' in spine_props or 'linewidth' in spine_props:
|
|
180
|
-
try:
|
|
181
|
-
lw = spine_props.get('lw') or spine_props.get('linewidth')
|
|
182
|
-
sp.set_linewidth(lw)
|
|
183
|
-
except Exception:
|
|
184
|
-
pass
|
|
185
|
-
if 'color' in spine_props:
|
|
186
|
-
try:
|
|
187
|
-
sp.set_edgecolor(spine_props['color'])
|
|
188
|
-
except Exception:
|
|
189
|
-
pass
|
|
190
|
-
if 'visible' in spine_props:
|
|
191
|
-
try:
|
|
192
|
-
sp.set_visible(spine_props['visible'])
|
|
193
|
-
except Exception:
|
|
194
|
-
pass
|
|
195
|
-
|
|
196
|
-
# Enforce compatibility between style/geom ro state and current figure ro state.
|
|
197
|
-
# Styles saved from a plot using --ro (swapped x/y) must not be applied to a non-ro plot, and vice versa.
|
|
198
|
-
file_ro = bool(cfg.get('ro_active', False))
|
|
199
|
-
current_ro = bool(getattr(fig, '_ro_active', False))
|
|
200
|
-
if file_ro != current_ro:
|
|
201
|
-
if file_ro:
|
|
202
|
-
print("Warning: XY style/geometry file was saved with --ro (swapped x/y axes); batch plot is not using --ro.")
|
|
203
|
-
else:
|
|
204
|
-
print("Warning: XY style/geometry file was saved without --ro; batch plot is treated as non-ro.")
|
|
205
|
-
print("Skipping style/geometry in batch mode to avoid corrupting axis orientation.")
|
|
206
|
-
return
|
|
65
|
+
"""Apply XY style/geometry via the canonical style module (batch/CLI path)."""
|
|
66
|
+
from .plot_modes.common.style_routing import apply_xy_style_dict
|
|
207
67
|
|
|
208
|
-
|
|
209
|
-
wasd_cfg = cfg.get('wasd_state', {})
|
|
210
|
-
if wasd_cfg:
|
|
211
|
-
# Top ticks (W)
|
|
212
|
-
if 'top' in wasd_cfg:
|
|
213
|
-
top_cfg = wasd_cfg['top']
|
|
214
|
-
if isinstance(top_cfg, dict):
|
|
215
|
-
ticks_on = top_cfg.get('ticks', False)
|
|
216
|
-
labels_on = top_cfg.get('labels', False)
|
|
217
|
-
ax.tick_params(axis='x', top=ticks_on, labeltop=labels_on)
|
|
218
|
-
# Left ticks (A)
|
|
219
|
-
if 'left' in wasd_cfg:
|
|
220
|
-
left_cfg = wasd_cfg['left']
|
|
221
|
-
if isinstance(left_cfg, dict):
|
|
222
|
-
ticks_on = left_cfg.get('ticks', False)
|
|
223
|
-
labels_on = left_cfg.get('labels', False)
|
|
224
|
-
ax.tick_params(axis='y', left=ticks_on, labelleft=labels_on)
|
|
225
|
-
# Bottom ticks (S)
|
|
226
|
-
if 'bottom' in wasd_cfg:
|
|
227
|
-
bottom_cfg = wasd_cfg['bottom']
|
|
228
|
-
if isinstance(bottom_cfg, dict):
|
|
229
|
-
ticks_on = bottom_cfg.get('ticks', True)
|
|
230
|
-
labels_on = bottom_cfg.get('labels', True)
|
|
231
|
-
ax.tick_params(axis='x', bottom=ticks_on, labelbottom=labels_on)
|
|
232
|
-
# Right ticks (D)
|
|
233
|
-
if 'right' in wasd_cfg:
|
|
234
|
-
right_cfg = wasd_cfg['right']
|
|
235
|
-
if isinstance(right_cfg, dict):
|
|
236
|
-
ticks_on = right_cfg.get('ticks', False)
|
|
237
|
-
labels_on = right_cfg.get('labels', False)
|
|
238
|
-
ax.tick_params(axis='y', right=ticks_on, labelright=labels_on)
|
|
239
|
-
|
|
240
|
-
# Apply rotation
|
|
241
|
-
rotation_cfg = cfg.get('rotation', {})
|
|
242
|
-
if rotation_cfg:
|
|
243
|
-
x_rotation = rotation_cfg.get('x')
|
|
244
|
-
y_rotation = rotation_cfg.get('y')
|
|
245
|
-
if x_rotation is not None:
|
|
246
|
-
try:
|
|
247
|
-
for label in ax.get_xticklabels():
|
|
248
|
-
label.set_rotation(x_rotation)
|
|
249
|
-
except Exception:
|
|
250
|
-
pass
|
|
251
|
-
if y_rotation is not None:
|
|
252
|
-
try:
|
|
253
|
-
for label in ax.get_yticklabels():
|
|
254
|
-
label.set_rotation(y_rotation)
|
|
255
|
-
except Exception:
|
|
256
|
-
pass
|
|
257
|
-
|
|
258
|
-
except Exception as e:
|
|
259
|
-
print(f"Warning: Error applying style: {e}")
|
|
68
|
+
apply_xy_style_dict(cfg, fig, ax)
|
|
260
69
|
|
|
261
70
|
|
|
262
|
-
def _apply_ec_style(fig, ax, cfg: dict):
|
|
263
|
-
"""Apply style
|
|
264
|
-
|
|
265
|
-
Applies formatting from .bps/.bpsg files including fonts, colors,
|
|
266
|
-
tick parameters, and geometry (if present in .bpsg files).
|
|
267
|
-
|
|
268
|
-
Args:
|
|
269
|
-
fig: Matplotlib figure object
|
|
270
|
-
ax: Matplotlib axes object
|
|
271
|
-
cfg: Style configuration dictionary
|
|
272
|
-
"""
|
|
273
|
-
try:
|
|
274
|
-
# Enforce compatibility between style/geom ro state and current figure ro state.
|
|
275
|
-
file_ro = bool(cfg.get('ro_active', False))
|
|
276
|
-
current_ro = bool(getattr(fig, '_ro_active', False))
|
|
277
|
-
if file_ro != current_ro:
|
|
278
|
-
if file_ro:
|
|
279
|
-
print("Warning: EC style/geometry file was saved with --ro (swapped x/y axes); batch EC plot is not using --ro.")
|
|
280
|
-
else:
|
|
281
|
-
print("Warning: EC style/geometry file was saved without --ro; batch EC plot is treated as non-ro.")
|
|
282
|
-
print("Skipping EC style/geometry in batch mode to avoid corrupting axis orientation.")
|
|
283
|
-
return
|
|
71
|
+
def _apply_ec_style(fig, ax, cfg: dict, *, cycle_lines=None, file_data=None, is_multi_file: bool = False):
|
|
72
|
+
"""Apply EC style/geometry via the canonical style module (batch/CLI path)."""
|
|
73
|
+
from .plot_modes.common.style_routing import apply_ec_style_dict
|
|
284
74
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
plt.rcParams['font.size'] = size
|
|
294
|
-
|
|
295
|
-
# Apply figure size if present
|
|
296
|
-
fig_cfg = cfg.get('figure', {})
|
|
297
|
-
if fig_cfg:
|
|
298
|
-
canvas_size = fig_cfg.get('canvas_size')
|
|
299
|
-
if canvas_size and isinstance(canvas_size, (list, tuple)) and len(canvas_size) == 2:
|
|
300
|
-
try:
|
|
301
|
-
fig.set_size_inches(canvas_size[0], canvas_size[1])
|
|
302
|
-
except Exception:
|
|
303
|
-
pass
|
|
304
|
-
|
|
305
|
-
# Apply tick parameters
|
|
306
|
-
ticks_cfg = cfg.get('ticks', {})
|
|
307
|
-
if ticks_cfg:
|
|
308
|
-
# Tick widths
|
|
309
|
-
widths = ticks_cfg.get('widths', {})
|
|
310
|
-
if widths.get('x_major') is not None:
|
|
311
|
-
ax.tick_params(axis='x', which='major', width=widths['x_major'])
|
|
312
|
-
if widths.get('x_minor') is not None:
|
|
313
|
-
ax.tick_params(axis='x', which='minor', width=widths['x_minor'])
|
|
314
|
-
if widths.get('y_major') is not None or widths.get('ly_major') is not None:
|
|
315
|
-
w = widths.get('y_major') or widths.get('ly_major')
|
|
316
|
-
ax.tick_params(axis='y', which='major', width=w)
|
|
317
|
-
if widths.get('y_minor') is not None or widths.get('ly_minor') is not None:
|
|
318
|
-
w = widths.get('y_minor') or widths.get('ly_minor')
|
|
319
|
-
ax.tick_params(axis='y', which='minor', width=w)
|
|
320
|
-
|
|
321
|
-
# Tick lengths
|
|
322
|
-
lengths = ticks_cfg.get('lengths', {})
|
|
323
|
-
if lengths.get('major') is not None:
|
|
324
|
-
ax.tick_params(axis='both', which='major', length=lengths['major'])
|
|
325
|
-
if lengths.get('minor') is not None:
|
|
326
|
-
ax.tick_params(axis='both', which='minor', length=lengths['minor'])
|
|
327
|
-
|
|
328
|
-
# Tick direction
|
|
329
|
-
direction = ticks_cfg.get('direction')
|
|
330
|
-
if direction:
|
|
331
|
-
ax.tick_params(axis='both', which='both', direction=direction)
|
|
332
|
-
|
|
333
|
-
# Apply geometry if present (for .bpsg files)
|
|
334
|
-
kind = cfg.get('kind', '')
|
|
335
|
-
if 'geom' in kind.lower() and 'geometry' in cfg:
|
|
336
|
-
geom = cfg.get('geometry', {})
|
|
337
|
-
if geom.get('xlabel'):
|
|
338
|
-
ax.set_xlabel(geom['xlabel'])
|
|
339
|
-
if geom.get('ylabel'):
|
|
340
|
-
ax.set_ylabel(geom['ylabel'])
|
|
341
|
-
if 'xlim' in geom and isinstance(geom['xlim'], (list, tuple)) and len(geom['xlim']) == 2:
|
|
342
|
-
try:
|
|
343
|
-
ax.set_xlim(geom['xlim'][0], geom['xlim'][1])
|
|
344
|
-
except Exception:
|
|
345
|
-
pass
|
|
346
|
-
if 'ylim' in geom and isinstance(geom['ylim'], (list, tuple)) and len(geom['ylim']) == 2:
|
|
347
|
-
try:
|
|
348
|
-
ax.set_ylim(geom['ylim'][0], geom['ylim'][1])
|
|
349
|
-
except Exception:
|
|
350
|
-
pass
|
|
351
|
-
|
|
352
|
-
# Apply line colors if available (for GC/CV/dQdV modes)
|
|
353
|
-
lines_cfg = cfg.get('lines', [])
|
|
354
|
-
if lines_cfg and len(ax.lines) > 0:
|
|
355
|
-
for entry in lines_cfg:
|
|
356
|
-
idx = entry.get('index')
|
|
357
|
-
if idx is not None and 0 <= idx < len(ax.lines):
|
|
358
|
-
ln = ax.lines[idx]
|
|
359
|
-
if 'color' in entry:
|
|
360
|
-
try:
|
|
361
|
-
ln.set_color(entry['color'])
|
|
362
|
-
except Exception:
|
|
363
|
-
pass
|
|
364
|
-
if 'linewidth' in entry:
|
|
365
|
-
try:
|
|
366
|
-
ln.set_linewidth(entry['linewidth'])
|
|
367
|
-
except Exception:
|
|
368
|
-
pass
|
|
369
|
-
if 'linestyle' in entry:
|
|
370
|
-
try:
|
|
371
|
-
ln.set_linestyle(entry['linestyle'])
|
|
372
|
-
except Exception:
|
|
373
|
-
pass
|
|
374
|
-
|
|
375
|
-
# Apply spine configuration
|
|
376
|
-
spines_cfg = cfg.get('spines', {})
|
|
377
|
-
for spine_name, spine_props in spines_cfg.items():
|
|
378
|
-
if spine_name in ax.spines:
|
|
379
|
-
sp = ax.spines[spine_name]
|
|
380
|
-
if 'lw' in spine_props or 'linewidth' in spine_props:
|
|
381
|
-
try:
|
|
382
|
-
lw = spine_props.get('lw') or spine_props.get('linewidth')
|
|
383
|
-
sp.set_linewidth(lw)
|
|
384
|
-
except Exception:
|
|
385
|
-
pass
|
|
386
|
-
if 'color' in spine_props:
|
|
387
|
-
try:
|
|
388
|
-
sp.set_edgecolor(spine_props['color'])
|
|
389
|
-
except Exception:
|
|
390
|
-
pass
|
|
391
|
-
if 'visible' in spine_props:
|
|
392
|
-
try:
|
|
393
|
-
sp.set_visible(spine_props['visible'])
|
|
394
|
-
except Exception:
|
|
395
|
-
pass
|
|
396
|
-
|
|
397
|
-
# Apply WASD state (tick visibility)
|
|
398
|
-
wasd_cfg = cfg.get('wasd_state', {})
|
|
399
|
-
if wasd_cfg:
|
|
400
|
-
# Top ticks (W)
|
|
401
|
-
if 'top' in wasd_cfg:
|
|
402
|
-
ax.tick_params(top=wasd_cfg['top'], labeltop=wasd_cfg['top'])
|
|
403
|
-
# Left ticks (A)
|
|
404
|
-
if 'left' in wasd_cfg:
|
|
405
|
-
ax.tick_params(left=wasd_cfg['left'], labelleft=wasd_cfg['left'])
|
|
406
|
-
# Bottom ticks (S)
|
|
407
|
-
if 'bottom' in wasd_cfg:
|
|
408
|
-
ax.tick_params(bottom=wasd_cfg['bottom'], labelbottom=wasd_cfg['bottom'])
|
|
409
|
-
# Right ticks (D)
|
|
410
|
-
if 'right' in wasd_cfg:
|
|
411
|
-
ax.tick_params(right=wasd_cfg['right'], labelright=wasd_cfg['right'])
|
|
412
|
-
|
|
413
|
-
# Apply rotation
|
|
414
|
-
rotation_cfg = cfg.get('rotation', {})
|
|
415
|
-
if rotation_cfg:
|
|
416
|
-
x_rotation = rotation_cfg.get('x')
|
|
417
|
-
y_rotation = rotation_cfg.get('y')
|
|
418
|
-
if x_rotation is not None:
|
|
419
|
-
try:
|
|
420
|
-
for label in ax.get_xticklabels():
|
|
421
|
-
label.set_rotation(x_rotation)
|
|
422
|
-
except Exception:
|
|
423
|
-
pass
|
|
424
|
-
if y_rotation is not None:
|
|
425
|
-
try:
|
|
426
|
-
for label in ax.get_yticklabels():
|
|
427
|
-
label.set_rotation(y_rotation)
|
|
428
|
-
except Exception:
|
|
429
|
-
pass
|
|
430
|
-
|
|
431
|
-
except Exception as e:
|
|
432
|
-
print(f"Warning: Error applying style: {e}")
|
|
75
|
+
apply_ec_style_dict(
|
|
76
|
+
cfg,
|
|
77
|
+
fig,
|
|
78
|
+
ax,
|
|
79
|
+
cycle_lines=cycle_lines,
|
|
80
|
+
file_data=file_data,
|
|
81
|
+
is_multi_file=is_multi_file,
|
|
82
|
+
)
|
|
433
83
|
|
|
434
84
|
|
|
435
85
|
def batch_process(directory: str, args):
|
|
@@ -1105,7 +755,7 @@ def batch_process_ec(directory: str, args):
|
|
|
1105
755
|
ax_b.set_ylabel('Potential (V)')
|
|
1106
756
|
ax_b.set_title(f"{fname}")
|
|
1107
757
|
legend = ax_b.legend(loc='best', fontsize='small', framealpha=0.8, title='Cycle')
|
|
1108
|
-
legend
|
|
758
|
+
sync_legend_title_fontsize(legend)
|
|
1109
759
|
|
|
1110
760
|
# ---- CV Mode ----
|
|
1111
761
|
elif mode == 'cv':
|
|
@@ -1143,7 +793,7 @@ def batch_process_ec(directory: str, args):
|
|
|
1143
793
|
ax_b.set_ylabel('Current (mA)')
|
|
1144
794
|
ax_b.set_title(f"{fname}")
|
|
1145
795
|
legend = ax_b.legend(loc='best', fontsize='small', framealpha=0.8, title='Cycle')
|
|
1146
|
-
legend
|
|
796
|
+
sync_legend_title_fontsize(legend)
|
|
1147
797
|
|
|
1148
798
|
# ---- dQdV Mode ----
|
|
1149
799
|
elif mode == 'dqdv':
|
|
@@ -1251,7 +901,7 @@ def batch_process_ec(directory: str, args):
|
|
|
1251
901
|
ax_b.set_ylabel(y_label)
|
|
1252
902
|
ax_b.set_title(f"{fname}")
|
|
1253
903
|
legend = ax_b.legend(loc='best', fontsize='small', framealpha=0.8, title='Cycle')
|
|
1254
|
-
legend
|
|
904
|
+
sync_legend_title_fontsize(legend)
|
|
1255
905
|
|
|
1256
906
|
# ---- CPC / EPC Mode ----
|
|
1257
907
|
elif mode in ('cpc', 'epc'):
|
|
@@ -1458,21 +1108,9 @@ def _load_histo_style_file(style_path: str) -> dict | None:
|
|
|
1458
1108
|
|
|
1459
1109
|
def _apply_histo_batch_style(fig, ax, state, cfg: dict) -> None:
|
|
1460
1110
|
"""Apply visual settings from a .bpsh file without replacing histogram data."""
|
|
1461
|
-
from .plot_modes.histo.
|
|
1462
|
-
from .plot_modes.histo.spines import apply_histo_spine_snapshot
|
|
1463
|
-
from .plot_modes.histo.fonts import sync_histo_font_rcparams
|
|
1464
|
-
from .plot_modes.histo.plot import apply_histo_geometry, refresh_histo_figure
|
|
1111
|
+
from .plot_modes.histo.session import apply_histo_style_snapshot
|
|
1465
1112
|
|
|
1466
|
-
|
|
1467
|
-
saved_source = state.source_path
|
|
1468
|
-
restored = _restore_snapshot(cfg)
|
|
1469
|
-
state.style = restored.style
|
|
1470
|
-
state.setup = saved_setup
|
|
1471
|
-
state.source_path = saved_source
|
|
1472
|
-
sync_histo_font_rcparams(state)
|
|
1473
|
-
apply_histo_spine_snapshot(fig, ax, cfg)
|
|
1474
|
-
apply_histo_geometry(fig, ax, state)
|
|
1475
|
-
refresh_histo_figure(fig, ax, state)
|
|
1113
|
+
apply_histo_style_snapshot(fig, ax, state, cfg)
|
|
1476
1114
|
|
|
1477
1115
|
|
|
1478
1116
|
def _save_histo_batch_figure(fig, ax, out_path: str) -> None:
|
|
@@ -1627,9 +1265,6 @@ def batch_process_histo(directory: str, args, *, only_files: list[str] | None =
|
|
|
1627
1265
|
print(f" Applying style from {base_name}.bpsh")
|
|
1628
1266
|
_apply_histo_batch_style(fig_b, ax_b, state, file_style_cfg)
|
|
1629
1267
|
|
|
1630
|
-
if not state.style.title:
|
|
1631
|
-
ax_b.set_title(fname)
|
|
1632
|
-
|
|
1633
1268
|
out_name = os.path.splitext(fname)[0] + f"_histo.{output_format}"
|
|
1634
1269
|
out_path = os.path.join(out_dir, out_name)
|
|
1635
1270
|
target = _confirm_overwrite(out_path)
|
|
@@ -99,7 +99,6 @@ import matplotlib.cm as cm # type: ignore[import-untyped]
|
|
|
99
99
|
import matplotlib.colors as mcolors # type: ignore[import-untyped]
|
|
100
100
|
from matplotlib.ticker import AutoMinorLocator, NullFormatter # type: ignore[import-untyped]
|
|
101
101
|
from matplotlib.colors import to_rgb, rgb_to_hsv, hsv_to_rgb # type: ignore[import-untyped]
|
|
102
|
-
from matplotlib.colors import to_rgb # type: ignore[import-untyped]
|
|
103
102
|
|
|
104
103
|
# Try to import optional interactive menus
|
|
105
104
|
try:
|
|
@@ -272,6 +272,20 @@ def color_block(color: Optional[str]) -> str:
|
|
|
272
272
|
return "[??]"
|
|
273
273
|
|
|
274
274
|
|
|
275
|
+
def format_color_listing(color) -> str:
|
|
276
|
+
"""Return ``<swatch> <hex>`` for menu listings (curves, saved colors, etc.)."""
|
|
277
|
+
if color is None:
|
|
278
|
+
return f"{color_block(None)} --"
|
|
279
|
+
try:
|
|
280
|
+
rgba = mcolors.to_rgba(color)
|
|
281
|
+
return f"{color_block(color)} {mcolors.to_hex(rgba)}"
|
|
282
|
+
except Exception:
|
|
283
|
+
text = str(color).strip()
|
|
284
|
+
if not text:
|
|
285
|
+
return f"{color_block(None)} --"
|
|
286
|
+
return f"{color_block(text)} {text}"
|
|
287
|
+
|
|
288
|
+
|
|
275
289
|
def color_bar(colors: Sequence[str]) -> str:
|
|
276
290
|
"""Return a string of adjacent color blocks."""
|
|
277
291
|
blocks = [color_block(col) for col in colors if col]
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.8.48] - 2026-07-29
|
|
4
|
+
- Bug fixes on session reload
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## [1.8.47] - 2026-07-21
|
|
8
|
+
- Added Batch Interactive mode, loading multiple saved .pkl sessions
|
|
9
|
+
(must be the same type, e.g All are GC) to edit basic styles and
|
|
10
|
+
geometries of all files at once.
|
|
11
|
+
Example:
|
|
12
|
+
batplot gc1.pkl gc2.pkl gc3.pkl
|
|
13
|
+
- Bug fixes
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
## [1.8.46] - 2026-07-07
|
|
4
17
|
- Added Histogram mode, use batplot -h to learn more
|
|
5
18
|
|