batplot 1.8.45__tar.gz → 1.8.46__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 (161) hide show
  1. {batplot-1.8.45 → batplot-1.8.46}/DEVELOPING.md +15 -1
  2. batplot-1.8.46/MANIFEST.in +4 -0
  3. {batplot-1.8.45 → batplot-1.8.46}/PKG-INFO +87 -5
  4. {batplot-1.8.45 → batplot-1.8.46}/README.md +86 -4
  5. {batplot-1.8.45 → batplot-1.8.46}/batplot/__init__.py +1 -1
  6. {batplot-1.8.45 → batplot-1.8.46}/batplot/_mpl_backend.py +90 -41
  7. {batplot-1.8.45 → batplot-1.8.46}/batplot/args.py +73 -5
  8. {batplot-1.8.45 → batplot-1.8.46}/batplot/batch.py +245 -1
  9. {batplot-1.8.45 → batplot-1.8.46}/batplot/batplot.py +29 -12
  10. {batplot-1.8.45 → batplot-1.8.46}/batplot/canvas_interactive.py +2 -0
  11. {batplot-1.8.45 → batplot-1.8.46}/batplot/cli.py +10 -0
  12. batplot-1.8.46/batplot/cli_save.py +375 -0
  13. {batplot-1.8.45 → batplot-1.8.46}/batplot/data/CHANGELOG.md +4 -0
  14. batplot-1.8.46/batplot/data/latest_release_notes.json +7 -0
  15. {batplot-1.8.45 → batplot-1.8.46}/batplot/dev_upgrade.py +457 -142
  16. batplot-1.8.46/batplot/plot_modes/batch_session/__init__.py +5 -0
  17. batplot-1.8.46/batplot/plot_modes/batch_session/batch_commands.py +128 -0
  18. batplot-1.8.46/batplot/plot_modes/batch_session/batch_io.py +178 -0
  19. batplot-1.8.46/batplot/plot_modes/batch_session/common.py +139 -0
  20. batplot-1.8.46/batplot/plot_modes/batch_session/kinds.py +45 -0
  21. batplot-1.8.46/batplot/plot_modes/batch_session/load.py +204 -0
  22. batplot-1.8.46/batplot/plot_modes/batch_session/menu_cpc.py +291 -0
  23. batplot-1.8.46/batplot/plot_modes/batch_session/menu_ec.py +318 -0
  24. batplot-1.8.46/batplot/plot_modes/batch_session/menu_histo.py +407 -0
  25. batplot-1.8.46/batplot/plot_modes/batch_session/menu_operando.py +292 -0
  26. batplot-1.8.46/batplot/plot_modes/batch_session/menu_xy.py +429 -0
  27. batplot-1.8.46/batplot/plot_modes/batch_session/routing.py +77 -0
  28. batplot-1.8.46/batplot/plot_modes/batch_session/xy_batch_helpers.py +171 -0
  29. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/spines.py +7 -0
  30. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/routing.py +50 -11
  31. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/routing.py +99 -31
  32. batplot-1.8.46/batplot/plot_modes/histo/__init__.py +5 -0
  33. batplot-1.8.46/batplot/plot_modes/histo/actions.py +436 -0
  34. batplot-1.8.46/batplot/plot_modes/histo/colors.py +200 -0
  35. batplot-1.8.46/batplot/plot_modes/histo/density_curve.py +152 -0
  36. batplot-1.8.46/batplot/plot_modes/histo/fonts.py +77 -0
  37. batplot-1.8.46/batplot/plot_modes/histo/interactive.py +502 -0
  38. batplot-1.8.46/batplot/plot_modes/histo/labels.py +101 -0
  39. batplot-1.8.46/batplot/plot_modes/histo/load.py +208 -0
  40. batplot-1.8.46/batplot/plot_modes/histo/plot.py +239 -0
  41. batplot-1.8.46/batplot/plot_modes/histo/routing.py +353 -0
  42. batplot-1.8.46/batplot/plot_modes/histo/session.py +61 -0
  43. batplot-1.8.46/batplot/plot_modes/histo/spines.py +291 -0
  44. batplot-1.8.46/batplot/plot_modes/histo/toggles.py +174 -0
  45. batplot-1.8.46/batplot/plot_modes/histo/wizard.py +188 -0
  46. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/plot.py +12 -3
  47. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/routing.py +40 -11
  48. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/session_routing.py +59 -55
  49. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/pipeline.py +47 -17
  50. {batplot-1.8.45 → batplot-1.8.46}/batplot/readers.py +24 -5
  51. {batplot-1.8.45 → batplot-1.8.46}/batplot/version_check.py +8 -7
  52. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/PKG-INFO +87 -5
  53. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/SOURCES.txt +33 -0
  54. {batplot-1.8.45 → batplot-1.8.46}/pyproject.toml +2 -2
  55. batplot-1.8.46/tests/test_batch_session.py +241 -0
  56. batplot-1.8.46/tests/test_bruker_intensity.py +25 -0
  57. batplot-1.8.46/tests/test_cli_save.py +72 -0
  58. {batplot-1.8.45 → batplot-1.8.46}/tests/test_cli_smoke.py +42 -0
  59. {batplot-1.8.45 → batplot-1.8.46}/tests/test_dev_upgrade.py +108 -0
  60. batplot-1.8.46/tests/test_histo.py +598 -0
  61. {batplot-1.8.45 → batplot-1.8.46}/tests/test_mpl_backend.py +39 -0
  62. batplot-1.8.45/MANIFEST.in +0 -3
  63. {batplot-1.8.45 → batplot-1.8.46}/LICENSE +0 -0
  64. {batplot-1.8.45 → batplot-1.8.46}/NOTICE +0 -0
  65. {batplot-1.8.45 → batplot-1.8.46}/batplot/cif.py +0 -0
  66. {batplot-1.8.45 → batplot-1.8.46}/batplot/color_utils.py +0 -0
  67. {batplot-1.8.45 → batplot-1.8.46}/batplot/config.py +0 -0
  68. {batplot-1.8.45 → batplot-1.8.46}/batplot/converters.py +0 -0
  69. {batplot-1.8.45 → batplot-1.8.46}/batplot/ec_common.py +0 -0
  70. {batplot-1.8.45 → batplot-1.8.46}/batplot/modes.py +0 -0
  71. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/__init__.py +0 -0
  72. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/__init__.py +0 -0
  73. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/axis_state.py +0 -0
  74. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/files.py +0 -0
  75. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/fonts.py +0 -0
  76. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/interactive_state.py +0 -0
  77. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/menu_rendering.py +0 -0
  78. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/menus.py +0 -0
  79. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/palettes.py +0 -0
  80. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/smoothing.py +0 -0
  81. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/sources.py +0 -0
  82. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/terminal.py +0 -0
  83. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/common/title_offsets.py +0 -0
  84. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/__init__.py +0 -0
  85. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/actions.py +0 -0
  86. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/colors.py +0 -0
  87. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/interactive.py +0 -0
  88. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/labels.py +0 -0
  89. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/legend.py +0 -0
  90. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/menu.py +0 -0
  91. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/session.py +0 -0
  92. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/cpc/snapshots.py +0 -0
  93. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/__init__.py +0 -0
  94. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/actions.py +0 -0
  95. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/colors.py +0 -0
  96. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/dqdv_2d.py +0 -0
  97. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/export.py +0 -0
  98. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/interactive.py +0 -0
  99. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/labels.py +0 -0
  100. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/legend.py +0 -0
  101. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/legend_order.py +0 -0
  102. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/line_style.py +0 -0
  103. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/menu.py +0 -0
  104. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/session.py +0 -0
  105. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/spine_colors.py +0 -0
  106. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/electrochem/style.py +0 -0
  107. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/__init__.py +0 -0
  108. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/actions.py +0 -0
  109. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/colors.py +0 -0
  110. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/grid.py +0 -0
  111. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/interactive.py +0 -0
  112. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/ions_axis.py +0 -0
  113. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/labels.py +0 -0
  114. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/layout.py +0 -0
  115. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/line_style.py +0 -0
  116. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/menu.py +0 -0
  117. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/peaks.py +0 -0
  118. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/session.py +0 -0
  119. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/style.py +0 -0
  120. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/operando/visibility.py +0 -0
  121. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/__init__.py +0 -0
  122. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/actions.py +0 -0
  123. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/arrange.py +0 -0
  124. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/axis_range.py +0 -0
  125. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/cif.py +0 -0
  126. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/colors.py +0 -0
  127. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/data_ops.py +0 -0
  128. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/derivative.py +0 -0
  129. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/game.py +0 -0
  130. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/interactive.py +0 -0
  131. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/labels.py +0 -0
  132. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/line_style.py +0 -0
  133. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/menu.py +0 -0
  134. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/peaks.py +0 -0
  135. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/session.py +0 -0
  136. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/smoothing.py +0 -0
  137. {batplot-1.8.45 → batplot-1.8.46}/batplot/plot_modes/xy/style.py +0 -0
  138. {batplot-1.8.45 → batplot-1.8.46}/batplot/plotting.py +0 -0
  139. {batplot-1.8.45 → batplot-1.8.46}/batplot/session.py +0 -0
  140. {batplot-1.8.45 → batplot-1.8.46}/batplot/showcol.py +0 -0
  141. {batplot-1.8.45 → batplot-1.8.46}/batplot/style.py +0 -0
  142. {batplot-1.8.45 → batplot-1.8.46}/batplot/ui.py +0 -0
  143. {batplot-1.8.45 → batplot-1.8.46}/batplot/utils.py +0 -0
  144. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/dependency_links.txt +0 -0
  145. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/entry_points.txt +0 -0
  146. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/requires.txt +0 -0
  147. {batplot-1.8.45 → batplot-1.8.46}/batplot.egg-info/top_level.txt +0 -0
  148. {batplot-1.8.45 → batplot-1.8.46}/setup.cfg +0 -0
  149. {batplot-1.8.45 → batplot-1.8.46}/setup.py +0 -0
  150. {batplot-1.8.45 → batplot-1.8.46}/tests/test_color_utils.py +0 -0
  151. {batplot-1.8.45 → batplot-1.8.46}/tests/test_common_files.py +0 -0
  152. {batplot-1.8.45 → batplot-1.8.46}/tests/test_common_palettes.py +0 -0
  153. {batplot-1.8.45 → batplot-1.8.46}/tests/test_contracts.py +0 -0
  154. {batplot-1.8.45 → batplot-1.8.46}/tests/test_cpc_roundtrip.py +0 -0
  155. {batplot-1.8.45 → batplot-1.8.46}/tests/test_csv_readers.py +0 -0
  156. {batplot-1.8.45 → batplot-1.8.46}/tests/test_ec_roundtrip.py +0 -0
  157. {batplot-1.8.45 → batplot-1.8.46}/tests/test_interactive_menu_smoke.py +0 -0
  158. {batplot-1.8.45 → batplot-1.8.46}/tests/test_interactive_state.py +0 -0
  159. {batplot-1.8.45 → batplot-1.8.46}/tests/test_operando_roundtrip.py +0 -0
  160. {batplot-1.8.45 → batplot-1.8.46}/tests/test_xy_modules.py +0 -0
  161. {batplot-1.8.45 → batplot-1.8.46}/tests/test_xy_roundtrip.py +0 -0
@@ -429,9 +429,23 @@ Before changing release behavior:
429
429
 
430
430
  1. Read `batplot/dev_upgrade.py`.
431
431
  2. Add or update tests in `tests/test_dev_upgrade.py`.
432
- 3. Confirm future files are included through package discovery or manifest rules.
432
+ 3. Confirm future files are included automatically:
433
+ - Python modules: `packages.find include = ["batplot*"]` plus
434
+ `validate_distribution_contents()` (every `batplot/**/*.py` must be in the wheel).
435
+ - Package data: drop files into `batplot/data/` (except `USER_MANUAL.md`);
436
+ `--dev-upgrade` runs `sync_pyproject_package_data()` to refresh
437
+ `[tool.setuptools.package-data]`.
438
+ - Git: `_git_stage_release_snapshot()` stages tracked updates and all untracked
439
+ non-ignored paths (no hand-maintained file list).
433
440
  4. Keep GitHub/PyPI conflict handling explicit and testable.
434
441
 
442
+ Canonical version files checked on every release:
443
+
444
+ - `batplot/__init__.py` (`__version__`)
445
+ - `pyproject.toml` (`[project] version`)
446
+ - `CITATION.cff` (`version: v…`)
447
+ - `batplot/data/latest_release_notes.json` (`version` field)
448
+
435
449
  ## Cross-Platform Checklist
436
450
 
437
451
  Use this checklist before finishing code changes:
@@ -0,0 +1,4 @@
1
+ include README.md LICENSE DEVELOPING.md
2
+ exclude batplot/data/USER_MANUAL.md
3
+ include batplot/data/CHANGELOG.md
4
+ include batplot/data/latest_release_notes.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.45
3
+ Version: 1.8.46
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
@@ -64,9 +64,10 @@ With a single line of command to easily plot publication-ready plots with custom
64
64
  - **Electrochemistry Plot**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
65
65
  - **1D XY plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
66
66
  - **Operando Contour plot**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
67
+ - **Histogram mode**: Column histograms from tabular `.csv`/`.txt` data (e.g. particle-size lists)
67
68
  - **Interactive plotting**: Real-time editing customized for each type of plottings
68
69
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
69
- - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
70
+ - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files (histogram: `.bpsh`)
70
71
  - **Batch Processing**: Export each file separately with `--all`
71
72
  - **Column preview**: `--showcol` prints numbered columns, header names when found, and the first 10 values per column (CSV, Excel, text, .mpt, .brml, Bruker .raw, etc.)
72
73
 
@@ -190,6 +191,21 @@ batplot file1.xy file2.xy --stack --i
190
191
  batplot allfiles --xaxis 2theta --xrange 15 75 --i
191
192
  ```
192
193
 
194
+ ### Session save without interactive menu (`--save`)
195
+
196
+ ```bash
197
+ # Single file: default .pkl name = data file stem; choose save folder when prompted
198
+ batplot pattern.xye --xaxis 2theta --save
199
+
200
+ # Batch: one session per file (default names), folder chosen once
201
+ batplot --all --xaxis 2theta --xrange 10 80 --save
202
+
203
+ # Combined plot (allfiles, operando, multi-file GC/CPC): you must name the session
204
+ batplot allfiles --xaxis q --save
205
+ batplot --operando --wl 0.25 --save
206
+ batplot file1.csv file2.csv --gc --save
207
+ ```
208
+
193
209
  ---
194
210
 
195
211
  ## Electrochemistry Mode
@@ -296,16 +312,80 @@ Operando column selection:
296
312
 
297
313
  ---
298
314
 
315
+ ## Histogram Mode
316
+
317
+ Histogram mode plots a single numeric column from tabular `.csv` or `.txt` files. It is designed for particle-size distributions and similar column data exported from spreadsheets or image-analysis tools.
318
+
319
+ ### Basic usage
320
+
321
+ ```bash
322
+ # Interactive wizard + styling menu
323
+ batplot sizes.csv --histo --i
324
+
325
+ # Non-interactive: column, range, bins, save figure
326
+ batplot data.txt --histo --histocol Length --xrange 0 16 --binwidth 1 --out hist.png
327
+
328
+ # Preview columns before choosing
329
+ batplot --showcol sizes.csv
330
+ ```
331
+
332
+ ### Startup wizard (`--i`)
333
+
334
+ 1. Choose the column to histogram (numbered list with preview)
335
+ 2. Set histogram range (`xmin xmax` or `auto`)
336
+ 3. Set bin width or `bins=N`
337
+
338
+ ### Batch export
339
+
340
+ Export each CSV/TXT file in the folder as a separate figure under `Figures/`:
341
+
342
+ ```bash
343
+ batplot --all --histo --histocol Length
344
+ batplot allfiles --histo --histocol 7 --binwidth 1
345
+ batplot histo_folder/ --histo --histocol Length
346
+
347
+ # Apply a shared histogram style (.bpsh)
348
+ batplot --all mystyle.bpsh --histo --histocol Length
349
+ ```
350
+
351
+ `--histocol` is required for batch export (column number or header name).
352
+
353
+ ### Batch interactive editing
354
+
355
+ Edit two or more histograms together (sync colors, fonts, export):
356
+
357
+ ```bash
358
+ batplot allfiles --histo --i
359
+ ```
360
+
361
+ If `--histocol` is omitted, the wizard runs on the first file and the same column/bin layout is reused for the rest.
362
+
363
+ ### Flags
364
+
365
+ | Flag | Description |
366
+ |------|-------------|
367
+ | `--histo` | Launch histogram mode |
368
+ | `--histocol N` | Column to histogram (1-indexed or header name) |
369
+ | `--xrange A B` | Histogram display range |
370
+ | `--binwidth W` | Width of each bin |
371
+ | `--bins N` | Number of equal-width bins |
372
+ | `--all` | Batch export each CSV/TXT file (requires `--histocol`) |
373
+ | `allfiles` | Expand all CSV/TXT in folder (batch export or `--i` batch edit) |
374
+
375
+ Style files: `.bpsh` (export with `p` in the interactive menu).
376
+
377
+ ---
378
+
299
379
  ## Plotting multiple files
300
380
 
301
381
  ```bash
302
382
  # All XY files in current directory on same figure
303
- batplot allfiles
304
- batplot allfiles --stack --i
383
+ batplot allfiles --xaxis 2theta --i
384
+ batplot allfiles --stack --gc --i
305
385
 
306
386
  # Only specific extension (natural-sorted)
307
387
  batplot allxyfiles
308
- batplot "/path/to/data" allnorfiles --i
388
+ batplot "/path/to/data" allnorfiles --xaxis energy --i
309
389
 
310
390
  # Explicit file list
311
391
  batplot file1.xye file2.qye structure.cif:1.54 --stack --i
@@ -351,6 +431,7 @@ With `--interactive`:
351
431
  - **Styling**: Line widths, markers, fonts
352
432
  - **Axes**: Labels, limits, ticks, spine styles
353
433
  - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images. Colors persist via `p` (print style), `i` (import), `s` (save session), `b` (undo)
434
+ - **Non-interactive session save**: `--save` (without `--i`) prompts for folder and filename—the same `.pkl` sessions as interactive `s`, with default names for single-file and `--all` batch runs
354
435
  - **Live Preview**: All changes update in real-time
355
436
 
356
437
  ---
@@ -362,6 +443,7 @@ batplot --h # General help
362
443
  batplot --h xy # XY mode guide
363
444
  batplot --h ec # Electrochemistry guide
364
445
  batplot --h op # Operando guide
446
+ batplot --h histo # Histogram mode guide
365
447
  batplot --v # Version and release notes
366
448
  batplot --m # Open illustrated manual
367
449
  ```
@@ -10,9 +10,10 @@ With a single line of command to easily plot publication-ready plots with custom
10
10
  - **Electrochemistry Plot**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
11
11
  - **1D XY plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
12
12
  - **Operando Contour plot**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
13
+ - **Histogram mode**: Column histograms from tabular `.csv`/`.txt` data (e.g. particle-size lists)
13
14
  - **Interactive plotting**: Real-time editing customized for each type of plottings
14
15
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
15
- - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
16
+ - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files (histogram: `.bpsh`)
16
17
  - **Batch Processing**: Export each file separately with `--all`
17
18
  - **Column preview**: `--showcol` prints numbered columns, header names when found, and the first 10 values per column (CSV, Excel, text, .mpt, .brml, Bruker .raw, etc.)
18
19
 
@@ -136,6 +137,21 @@ batplot file1.xy file2.xy --stack --i
136
137
  batplot allfiles --xaxis 2theta --xrange 15 75 --i
137
138
  ```
138
139
 
140
+ ### Session save without interactive menu (`--save`)
141
+
142
+ ```bash
143
+ # Single file: default .pkl name = data file stem; choose save folder when prompted
144
+ batplot pattern.xye --xaxis 2theta --save
145
+
146
+ # Batch: one session per file (default names), folder chosen once
147
+ batplot --all --xaxis 2theta --xrange 10 80 --save
148
+
149
+ # Combined plot (allfiles, operando, multi-file GC/CPC): you must name the session
150
+ batplot allfiles --xaxis q --save
151
+ batplot --operando --wl 0.25 --save
152
+ batplot file1.csv file2.csv --gc --save
153
+ ```
154
+
139
155
  ---
140
156
 
141
157
  ## Electrochemistry Mode
@@ -242,16 +258,80 @@ Operando column selection:
242
258
 
243
259
  ---
244
260
 
261
+ ## Histogram Mode
262
+
263
+ Histogram mode plots a single numeric column from tabular `.csv` or `.txt` files. It is designed for particle-size distributions and similar column data exported from spreadsheets or image-analysis tools.
264
+
265
+ ### Basic usage
266
+
267
+ ```bash
268
+ # Interactive wizard + styling menu
269
+ batplot sizes.csv --histo --i
270
+
271
+ # Non-interactive: column, range, bins, save figure
272
+ batplot data.txt --histo --histocol Length --xrange 0 16 --binwidth 1 --out hist.png
273
+
274
+ # Preview columns before choosing
275
+ batplot --showcol sizes.csv
276
+ ```
277
+
278
+ ### Startup wizard (`--i`)
279
+
280
+ 1. Choose the column to histogram (numbered list with preview)
281
+ 2. Set histogram range (`xmin xmax` or `auto`)
282
+ 3. Set bin width or `bins=N`
283
+
284
+ ### Batch export
285
+
286
+ Export each CSV/TXT file in the folder as a separate figure under `Figures/`:
287
+
288
+ ```bash
289
+ batplot --all --histo --histocol Length
290
+ batplot allfiles --histo --histocol 7 --binwidth 1
291
+ batplot histo_folder/ --histo --histocol Length
292
+
293
+ # Apply a shared histogram style (.bpsh)
294
+ batplot --all mystyle.bpsh --histo --histocol Length
295
+ ```
296
+
297
+ `--histocol` is required for batch export (column number or header name).
298
+
299
+ ### Batch interactive editing
300
+
301
+ Edit two or more histograms together (sync colors, fonts, export):
302
+
303
+ ```bash
304
+ batplot allfiles --histo --i
305
+ ```
306
+
307
+ If `--histocol` is omitted, the wizard runs on the first file and the same column/bin layout is reused for the rest.
308
+
309
+ ### Flags
310
+
311
+ | Flag | Description |
312
+ |------|-------------|
313
+ | `--histo` | Launch histogram mode |
314
+ | `--histocol N` | Column to histogram (1-indexed or header name) |
315
+ | `--xrange A B` | Histogram display range |
316
+ | `--binwidth W` | Width of each bin |
317
+ | `--bins N` | Number of equal-width bins |
318
+ | `--all` | Batch export each CSV/TXT file (requires `--histocol`) |
319
+ | `allfiles` | Expand all CSV/TXT in folder (batch export or `--i` batch edit) |
320
+
321
+ Style files: `.bpsh` (export with `p` in the interactive menu).
322
+
323
+ ---
324
+
245
325
  ## Plotting multiple files
246
326
 
247
327
  ```bash
248
328
  # All XY files in current directory on same figure
249
- batplot allfiles
250
- batplot allfiles --stack --i
329
+ batplot allfiles --xaxis 2theta --i
330
+ batplot allfiles --stack --gc --i
251
331
 
252
332
  # Only specific extension (natural-sorted)
253
333
  batplot allxyfiles
254
- batplot "/path/to/data" allnorfiles --i
334
+ batplot "/path/to/data" allnorfiles --xaxis energy --i
255
335
 
256
336
  # Explicit file list
257
337
  batplot file1.xye file2.qye structure.cif:1.54 --stack --i
@@ -297,6 +377,7 @@ With `--interactive`:
297
377
  - **Styling**: Line widths, markers, fonts
298
378
  - **Axes**: Labels, limits, ticks, spine styles
299
379
  - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images. Colors persist via `p` (print style), `i` (import), `s` (save session), `b` (undo)
380
+ - **Non-interactive session save**: `--save` (without `--i`) prompts for folder and filename—the same `.pkl` sessions as interactive `s`, with default names for single-file and `--all` batch runs
300
381
  - **Live Preview**: All changes update in real-time
301
382
 
302
383
  ---
@@ -308,6 +389,7 @@ batplot --h # General help
308
389
  batplot --h xy # XY mode guide
309
390
  batplot --h ec # Electrochemistry guide
310
391
  batplot --h op # Operando guide
392
+ batplot --h histo # Histogram mode guide
311
393
  batplot --v # Version and release notes
312
394
  batplot --m # Open illustrated manual
313
395
  ```
@@ -7,7 +7,7 @@ import importlib.abc
7
7
  import importlib.machinery
8
8
  import sys
9
9
 
10
- __version__ = "1.8.45"
10
+ __version__ = "1.8.46"
11
11
 
12
12
 
13
13
  _LEGACY_MODULE_ALIASES = {
@@ -77,6 +77,8 @@ def running_headless() -> bool:
77
77
 
78
78
  def wants_interactive_window(args) -> bool:
79
79
  """Return True when this CLI invocation should open an interactive figure."""
80
+ if args is None:
81
+ return False
80
82
  if getattr(args, "all", None) is not None:
81
83
  return False
82
84
  files = getattr(args, "files", None) or []
@@ -86,12 +88,17 @@ def wants_interactive_window(args) -> bool:
86
88
  return True
87
89
  if getattr(args, "interactive", False):
88
90
  return True
89
- # Headless export: mode flags with --out/--savefig do not need a GUI window.
91
+ # Headless export / session save: --out, --savefig, or --save (without --i).
90
92
  if getattr(args, "savefig", False) or getattr(args, "out", None):
91
93
  return False
92
- for attr in ("operando", "contour", "gc", "cv", "dqdv", "cpc", "epc"):
94
+ if getattr(args, "save", False) and not getattr(args, "interactive", False):
95
+ return False
96
+ for attr in ("operando", "contour", "gc", "cv", "dqdv", "cpc", "epc", "histo"):
93
97
  if getattr(args, attr, False):
94
98
  return True
99
+ # Default XY / multi-file diffraction view (no --out, no --all).
100
+ if files:
101
+ return True
95
102
  return False
96
103
 
97
104
 
@@ -128,26 +135,6 @@ def _backend_can_be_tried(name: str) -> bool:
128
135
  return True
129
136
 
130
137
 
131
- def _backend_setup_tips() -> list[str]:
132
- if sys.platform == "darwin":
133
- return [
134
- " export MPLBACKEND=MacOSX # built-in macOS backend",
135
- " export MPLBACKEND=TkAgg # if Tk is available",
136
- " export MPLBACKEND=QtAgg # if PyQt/PySide is installed",
137
- ]
138
- if sys.platform.startswith("win"):
139
- return [
140
- " set MPLBACKEND=TkAgg # cmd, if Tk is available",
141
- ' $env:MPLBACKEND="TkAgg" # PowerShell',
142
- " set MPLBACKEND=QtAgg # if PyQt/PySide is installed",
143
- ]
144
- return [
145
- " export MPLBACKEND=TkAgg # if python3-tk is installed",
146
- " export MPLBACKEND=QtAgg # if PyQt/PySide is installed",
147
- " export MPLBACKEND=Gtk3Agg # if GTK3 bindings are installed",
148
- ]
149
-
150
-
151
138
  def ensure_gui_backend(args=None) -> bool:
152
139
  """Switch from Agg to a GUI backend when an interactive window is expected.
153
140
 
@@ -181,20 +168,51 @@ def ensure_gui_backend(args=None) -> bool:
181
168
  return False
182
169
 
183
170
 
184
- def warn_if_noninteractive(context: str = "interactive menu") -> bool:
185
- """Print a helpful message when the backend cannot show a window."""
186
- if is_interactive_backend():
187
- return True
171
+ def _one_line_backend_workaround() -> str:
172
+ """Return a copy-paste command prefix for the current platform."""
173
+ if sys.platform == "darwin":
174
+ return "MPLBACKEND=MacOSX batplot ..."
175
+ if sys.platform.startswith("win"):
176
+ return 'set MPLBACKEND=TkAgg (cmd) or $env:MPLBACKEND="TkAgg" (PowerShell), then batplot ...'
177
+ return "MPLBACKEND=TkAgg batplot ..."
178
+
179
+
180
+ def _print_interactive_backend_help(context: str = "interactive menu") -> None:
181
+ """User-facing recovery steps when no GUI window can be opened."""
188
182
  try:
189
183
  backend = _mpl.get_backend()
190
184
  except Exception:
191
185
  backend = "unknown"
192
- print(f"Matplotlib backend '{backend}' is non-interactive; a window cannot be shown.")
193
- print("Tips: unset MPLBACKEND or set a GUI backend:")
194
- for line in _backend_setup_tips():
195
- print(line)
186
+ try:
187
+ from . import __version__ as _bp_version
188
+ except Exception:
189
+ _bp_version = "latest"
190
+
191
+ print()
192
+ print("Interactive plotting needs a display window, but batplot could not open one.")
193
+ print(f"(Matplotlib backend: {backend})")
196
194
  if context:
197
- print(f"The {context} requires an interactive backend.")
195
+ print(f"This affects: {context}.")
196
+ print()
197
+ print("Try these steps, in order:")
198
+ print(" 1. Upgrade batplot (fixes most cases):")
199
+ print(" pip install --upgrade batplot")
200
+ print(f" You need the latest batplot release (installed: {_bp_version}).")
201
+ print(" 2. If you cannot upgrade yet, force a GUI backend for this session:")
202
+ print(f" {_one_line_backend_workaround()}")
203
+ print(" 3. To save a figure without the interactive menu:")
204
+ print(" batplot ... --out figure.png (omit --i)")
205
+ print()
206
+ print("Advanced: if MPLBACKEND=Agg is set in your shell or conda env, unset it or override")
207
+ print("as in step 2. On headless servers (SSH with no display), use --out instead of --i.")
208
+ print()
209
+
210
+
211
+ def warn_if_noninteractive(context: str = "interactive menu") -> bool:
212
+ """Print a helpful message when the backend cannot show a window."""
213
+ if is_interactive_backend():
214
+ return True
215
+ _print_interactive_backend_help(context)
198
216
  return False
199
217
 
200
218
 
@@ -202,30 +220,61 @@ def require_interactive_display(
202
220
  args=None,
203
221
  *,
204
222
  context: str = "interactive menu",
205
- export_hint: str | None = "Or run without --interactive and use --out to save the figure.",
223
+ export_hint: str | None = None,
206
224
  ) -> bool:
207
225
  """Ensure a GUI backend, warn if unavailable, return True when display is possible."""
208
226
  ensure_gui_backend(args)
209
227
  if is_interactive_backend():
210
228
  return True
211
229
  warn_if_noninteractive(context)
212
- if export_hint:
213
- print(export_hint)
214
230
  return False
215
231
 
216
232
 
217
- def show_figure_if_possible(args=None, *, block: bool = False) -> bool:
218
- """Show the current figure when the backend supports windows."""
233
+ def show_figure_if_possible(args=None, *, block: bool = True) -> bool:
234
+ """Show the current figure when the backend supports windows.
235
+
236
+ Defaults to ``block=True`` so the window stays open until the user closes it.
237
+ Non-interactive view mode (no ``--i``) relies on this; ``block=False`` would
238
+ return immediately and the process would exit before the figure is visible.
239
+ """
219
240
  ensure_gui_backend(args)
220
241
  if not is_interactive_backend():
221
242
  if args is not None and not (getattr(args, "savefig", False) or getattr(args, "out", None)):
222
- try:
223
- backend = _mpl.get_backend()
224
- except Exception:
225
- backend = "unknown"
226
- print(f"Matplotlib backend '{backend}' is non-interactive; use --out to save the figure.")
243
+ print("No display window available. Save the figure with: batplot ... --out figure.png")
227
244
  return False
228
245
  import matplotlib.pyplot as plt
229
246
 
230
247
  plt.show(block=block)
231
248
  return True
249
+
250
+
251
+ def prime_interactive_figure(fig=None) -> None:
252
+ """Prepare a matplotlib window before an interactive menu (non-blocking)."""
253
+ import matplotlib.pyplot as plt
254
+
255
+ try:
256
+ plt.ion()
257
+ except Exception:
258
+ pass
259
+ if fig is not None:
260
+ try:
261
+ fig.canvas.draw_idle()
262
+ fig.canvas.flush_events()
263
+ except Exception:
264
+ pass
265
+ try:
266
+ plt.show(block=False)
267
+ except Exception:
268
+ pass
269
+
270
+
271
+ def hold_figure_open() -> None:
272
+ """Keep the current figure window open until the user closes it."""
273
+ if not is_interactive_backend():
274
+ return
275
+ import matplotlib.pyplot as plt
276
+
277
+ try:
278
+ plt.show()
279
+ except Exception:
280
+ pass