batplot 1.8.28__tar.gz → 1.8.29__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 (41) hide show
  1. {batplot-1.8.28/batplot.egg-info → batplot-1.8.29}/PKG-INFO +15 -2
  2. {batplot-1.8.28 → batplot-1.8.29}/README.md +14 -1
  3. {batplot-1.8.28 → batplot-1.8.29}/batplot/__init__.py +1 -1
  4. {batplot-1.8.28 → batplot-1.8.29}/batplot/args.py +11 -2
  5. {batplot-1.8.28 → batplot-1.8.29}/batplot/batplot.py +23 -12
  6. {batplot-1.8.28 → batplot-1.8.29}/batplot/converters.py +114 -6
  7. {batplot-1.8.28 → batplot-1.8.29}/batplot/data/CHANGELOG.md +6 -0
  8. {batplot-1.8.28 → batplot-1.8.29}/batplot/data/USER_MANUAL.md +15 -0
  9. {batplot-1.8.28 → batplot-1.8.29}/batplot/plotting.py +21 -7
  10. {batplot-1.8.28 → batplot-1.8.29}/batplot/version_check.py +5 -2
  11. {batplot-1.8.28 → batplot-1.8.29/batplot.egg-info}/PKG-INFO +15 -2
  12. {batplot-1.8.28 → batplot-1.8.29}/batplot.egg-info/SOURCES.txt +0 -1
  13. {batplot-1.8.28 → batplot-1.8.29}/pyproject.toml +1 -1
  14. batplot-1.8.28/USER_MANUAL.md +0 -723
  15. {batplot-1.8.28 → batplot-1.8.29}/LICENSE +0 -0
  16. {batplot-1.8.28 → batplot-1.8.29}/MANIFEST.in +0 -0
  17. {batplot-1.8.28 → batplot-1.8.29}/NOTICE +0 -0
  18. {batplot-1.8.28 → batplot-1.8.29}/batplot/batch.py +0 -0
  19. {batplot-1.8.28 → batplot-1.8.29}/batplot/cif.py +0 -0
  20. {batplot-1.8.28 → batplot-1.8.29}/batplot/cli.py +0 -0
  21. {batplot-1.8.28 → batplot-1.8.29}/batplot/color_utils.py +0 -0
  22. {batplot-1.8.28 → batplot-1.8.29}/batplot/config.py +0 -0
  23. {batplot-1.8.28 → batplot-1.8.29}/batplot/cpc_interactive.py +0 -0
  24. {batplot-1.8.28 → batplot-1.8.29}/batplot/dev_upgrade.py +0 -0
  25. {batplot-1.8.28 → batplot-1.8.29}/batplot/electrochem_interactive.py +0 -0
  26. {batplot-1.8.28 → batplot-1.8.29}/batplot/interactive.py +0 -0
  27. {batplot-1.8.28 → batplot-1.8.29}/batplot/manual.py +0 -0
  28. {batplot-1.8.28 → batplot-1.8.29}/batplot/modes.py +0 -0
  29. {batplot-1.8.28 → batplot-1.8.29}/batplot/operando.py +0 -0
  30. {batplot-1.8.28 → batplot-1.8.29}/batplot/operando_ec_interactive.py +0 -0
  31. {batplot-1.8.28 → batplot-1.8.29}/batplot/readers.py +0 -0
  32. {batplot-1.8.28 → batplot-1.8.29}/batplot/session.py +0 -0
  33. {batplot-1.8.28 → batplot-1.8.29}/batplot/style.py +0 -0
  34. {batplot-1.8.28 → batplot-1.8.29}/batplot/ui.py +0 -0
  35. {batplot-1.8.28 → batplot-1.8.29}/batplot/utils.py +0 -0
  36. {batplot-1.8.28 → batplot-1.8.29}/batplot.egg-info/dependency_links.txt +0 -0
  37. {batplot-1.8.28 → batplot-1.8.29}/batplot.egg-info/entry_points.txt +0 -0
  38. {batplot-1.8.28 → batplot-1.8.29}/batplot.egg-info/requires.txt +0 -0
  39. {batplot-1.8.28 → batplot-1.8.29}/batplot.egg-info/top_level.txt +0 -0
  40. {batplot-1.8.28 → batplot-1.8.29}/setup.cfg +0 -0
  41. {batplot-1.8.28 → batplot-1.8.29}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.28
3
+ Version: 1.8.29
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
@@ -112,6 +112,10 @@ batplot scan1.brml:1.5406 scan2.xye:0.7093
112
112
  # Convert and export to converted/ subfolder (q and Q equivalent)
113
113
  batplot data.xye --convert 1.54 q
114
114
  batplot data.qye --convert q 1.54
115
+
116
+ # With --readcol for custom column layout (e.g. 2θ in col 3, intensity in col 4)
117
+ batplot data.csv --readcol 3 4 --convert 1.54 q
118
+ batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q
115
119
  ```
116
120
 
117
121
  ### Stacking and normalization
@@ -152,6 +156,9 @@ batplot data.xy --readcol 1 2 1 3
152
156
 
153
157
  # Range: col 1 as x, cols 2–20 as 19 y-curves
154
158
  batplot file.txt --readcol 1 2-20
159
+
160
+ # With --convert: use custom columns when converting XRD data
161
+ batplot data.csv --readcol 3 4 --convert 1.54 q
155
162
  ```
156
163
 
157
164
  ### Derivatives and EXAFS
@@ -186,6 +193,8 @@ batplot allfiles --xaxis 2theta --xrange 15 75 --interactive
186
193
 
187
194
  ### Galvanostatic cycling (GC)
188
195
 
196
+ GC mode plots potential vs. capacity for each charge/discharge cycle—the primary visualization for battery cycling data. Batplot automatically detects cycles from Neware `.csv` or Biologic `.mpt` files, assigns each cycle a distinct color, and supports both specific capacity (mAh/g) and raw capacity. For `.mpt` files, pass `--mass` (mg) to compute specific capacity. Discontinuous or paused experiments are handled by splitting data into contiguous charge/discharge segments. Use the interactive menu to customize colors, visibility, and export.
197
+
189
198
  ```bash
190
199
  # From .csv (capacity in file)
191
200
  batplot battery.csv --gc
@@ -278,13 +287,17 @@ batplot file1.xye file2.qye structure.cif:1.54 --stack --interactive
278
287
 
279
288
  ## Batch export (--all)
280
289
 
281
- Export each file as a separate figure to `batplot_svg/`:
290
+ Export each file as a separate figure to `Figures/`:
282
291
 
283
292
  ```bash
284
293
  batplot --all
285
294
  batplot --all --format png
286
295
  batplot --all --xaxis 2theta --xrange 10 80
287
296
  batplot --all style.bps --gc --mass 7
297
+
298
+ # With --readcol for custom columns (put --readcol before style file)
299
+ batplot --all --readcol 2 3 --xaxis 2theta
300
+ batplot --all --readcol 2 3 style.bps --xaxis 2theta
288
301
  ```
289
302
 
290
303
  ---
@@ -61,6 +61,10 @@ batplot scan1.brml:1.5406 scan2.xye:0.7093
61
61
  # Convert and export to converted/ subfolder (q and Q equivalent)
62
62
  batplot data.xye --convert 1.54 q
63
63
  batplot data.qye --convert q 1.54
64
+
65
+ # With --readcol for custom column layout (e.g. 2θ in col 3, intensity in col 4)
66
+ batplot data.csv --readcol 3 4 --convert 1.54 q
67
+ batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q
64
68
  ```
65
69
 
66
70
  ### Stacking and normalization
@@ -101,6 +105,9 @@ batplot data.xy --readcol 1 2 1 3
101
105
 
102
106
  # Range: col 1 as x, cols 2–20 as 19 y-curves
103
107
  batplot file.txt --readcol 1 2-20
108
+
109
+ # With --convert: use custom columns when converting XRD data
110
+ batplot data.csv --readcol 3 4 --convert 1.54 q
104
111
  ```
105
112
 
106
113
  ### Derivatives and EXAFS
@@ -135,6 +142,8 @@ batplot allfiles --xaxis 2theta --xrange 15 75 --interactive
135
142
 
136
143
  ### Galvanostatic cycling (GC)
137
144
 
145
+ GC mode plots potential vs. capacity for each charge/discharge cycle—the primary visualization for battery cycling data. Batplot automatically detects cycles from Neware `.csv` or Biologic `.mpt` files, assigns each cycle a distinct color, and supports both specific capacity (mAh/g) and raw capacity. For `.mpt` files, pass `--mass` (mg) to compute specific capacity. Discontinuous or paused experiments are handled by splitting data into contiguous charge/discharge segments. Use the interactive menu to customize colors, visibility, and export.
146
+
138
147
  ```bash
139
148
  # From .csv (capacity in file)
140
149
  batplot battery.csv --gc
@@ -227,13 +236,17 @@ batplot file1.xye file2.qye structure.cif:1.54 --stack --interactive
227
236
 
228
237
  ## Batch export (--all)
229
238
 
230
- Export each file as a separate figure to `batplot_svg/`:
239
+ Export each file as a separate figure to `Figures/`:
231
240
 
232
241
  ```bash
233
242
  batplot --all
234
243
  batplot --all --format png
235
244
  batplot --all --xaxis 2theta --xrange 10 80
236
245
  batplot --all style.bps --gc --mass 7
246
+
247
+ # With --readcol for custom columns (put --readcol before style file)
248
+ batplot --all --readcol 2 3 --xaxis 2theta
249
+ batplot --all --readcol 2 3 style.bps --xaxis 2theta
237
250
  ```
238
251
 
239
252
  ---
@@ -1,5 +1,5 @@
1
1
  """batplot: Interactive plotting for battery data visualization."""
2
2
 
3
- __version__ = "1.8.28"
3
+ __version__ = "1.8.29"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -227,6 +227,9 @@ def _print_xy_help() -> None:
227
227
  " - <wl1> <wl2> : convert 2θ from wavelength1 to wavelength2\n"
228
228
  " - <wl> q or Q : convert 2θ (with wavelength) to Q space (q and Q equivalent)\n"
229
229
  " - q or Q <wl> : convert Q space to 2θ (with wavelength)\n"
230
+ " Works with --readcol for custom column layout (per-file, per-ext, or global):\n"
231
+ " batplot data.csv --readcol 3 4 --convert 1.54 q\n"
232
+ " batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q\n"
230
233
  " Examples:\n"
231
234
  " batplot file.xye --convert 1.54 0.25\n"
232
235
  " batplot file.xye --convert 1.54 q\n"
@@ -244,6 +247,7 @@ def _print_xy_help() -> None:
244
247
  " Multi-curve: file.xy --readcol 1 2 1 3 (plot cols 1,2 and 1,3 as two curves)\n"
245
248
  " Range: file.txt --readcol 1 2-20 (col 1 as x, cols 2..20 as 19 y-curves)\n"
246
249
  " With wavelength: file.xy:1.54 --readcol 2 3 (col 2 as 2θ, convert to Q using λ=1.54 Å)\n"
250
+ " With --convert: file.csv --readcol 3 4 --convert 1.54 q (custom cols for conversion)\n"
247
251
  " --readcolxy <x> <y> : read columns for .xy files only\n"
248
252
  " --readcolxye <x> <y> : read columns for .xye files only\n"
249
253
  " --readcolqye <x> <y> : read columns for .qye files only\n"
@@ -574,17 +578,22 @@ def parse_args(argv=None):
574
578
  # does not consume it. When --readcol appears before any file (global),
575
579
  # store in global_readcol_expanded for post-parse.
576
580
  # Keys use the exact file token (e.g. "file.xy:1.54") for wavelength match.
581
+ # Style files (.bps, .bpsg, .bpcfg) are NOT treated as file tokens so that
582
+ # "batplot --all style.bps --readcol 2 3" uses global readcol, not per-file.
577
583
  # ====================================================================
578
584
  readcol_by_file = {}
579
585
  global_readcol_expanded = None
580
586
  filtered_argv = []
581
587
  last_file_token = None
588
+ _STYLE_EXTENSIONS = ('.bps', '.bpsg', '.bpcfg')
582
589
  i = 0
583
590
  while i < len(argv):
584
591
  arg = argv[i]
585
- # Track non-option tokens as potential file specs
592
+ # Track non-option tokens as potential file specs (exclude style files)
586
593
  if not arg.startswith('-'):
587
- last_file_token = arg
594
+ arg_lower = arg.lower()
595
+ if not arg_lower.endswith(_STYLE_EXTENSIONS):
596
+ last_file_token = arg
588
597
  if arg == '--readcol' and i + 1 < len(argv):
589
598
  tokens = []
590
599
  j = i + 1
@@ -3416,7 +3416,7 @@ def batplot_main() -> int: # type: ignore
3416
3416
  print("Error: --convert requires file(s) to convert")
3417
3417
  exit(1)
3418
3418
  from_param, to_param = args.convert
3419
- convert_xrd_data(args.files, from_param, to_param)
3419
+ convert_xrd_data(args.files, from_param, to_param, args=args)
3420
3420
  exit()
3421
3421
 
3422
3422
  # ---------------- Plotting ----------------
@@ -4035,7 +4035,17 @@ def batplot_main() -> int: # type: ignore
4035
4035
  y_plotted = y_plot_offset
4036
4036
 
4037
4037
  target_ax = ax2 if is_right_y else ax
4038
- target_ax.plot(x_plotted, y_plotted, "-", lw=1, alpha=0.8)
4038
+ # With --ry: assign explicit colors so plot, Colors menu (c), labels, and p/i/s/b stay consistent.
4039
+ # Matplotlib twinx uses a separate color cycle; explicit colors avoid mismatch across axes.
4040
+ if right_y_data_indices:
4041
+ try:
4042
+ curve_idx = len(y_data_list)
4043
+ curve_color = plt.cm.tab10(curve_idx % 10)
4044
+ target_ax.plot(x_plotted, y_plotted, "-", lw=1, alpha=0.8, color=curve_color)
4045
+ except Exception:
4046
+ target_ax.plot(x_plotted, y_plotted, "-", lw=1, alpha=0.8)
4047
+ else:
4048
+ target_ax.plot(x_plotted, y_plotted, "-", lw=1, alpha=0.8)
4039
4049
  x_data_list.append(x_plotted)
4040
4050
  y_data_list.append(y_plotted.copy())
4041
4051
  labels_list.append(curve_label)
@@ -4102,19 +4112,11 @@ def batplot_main() -> int: # type: ignore
4102
4112
  transform=ax.transAxes)
4103
4113
  label_text_objects.append(txt)
4104
4114
 
4105
- # Ensure consistent initial placement (especially for stacked mode)
4106
- update_labels(ax, y_data_list, label_text_objects, args.stack, False)
4107
-
4108
- # Initialize curve names visibility (default to visible)
4109
- fig._curve_names_visible = True
4110
- # Initialize stack label position (default to top/max)
4111
- fig._stack_label_at_bottom = False
4112
- fig._label_anchor_left = False
4113
- # Right y-axis state (--ry): ax2, curve indices, and curve->line mapping
4115
+ # Right y-axis state (--ry): build curve->line mapping BEFORE first update_labels
4116
+ # so label colors correctly match curves on both axes (plotting.py uses fig._xy_lines_by_curve)
4114
4117
  fig._xy_ax2 = ax2
4115
4118
  fig._xy_use_top_x = bool(getattr(args, 'txaxis', False))
4116
4119
  fig._xy_right_y_curve_indices = frozenset(right_y_curve_indices)
4117
- # Build curve index -> line mapping for interactive (ax.lines vs ax2.lines)
4118
4120
  _lines_by_curve = []
4119
4121
  _left_indices = sorted(i for i in range(len(y_data_list)) if i not in right_y_curve_indices)
4120
4122
  _right_sorted = sorted(right_y_curve_indices)
@@ -4127,6 +4129,15 @@ def batplot_main() -> int: # type: ignore
4127
4129
  _lines_by_curve.append(ax.lines[k] if k < len(ax.lines) else None)
4128
4130
  fig._xy_lines_by_curve = _lines_by_curve
4129
4131
 
4132
+ # Ensure consistent initial placement (especially for stacked mode)
4133
+ update_labels(ax, y_data_list, label_text_objects, args.stack, False)
4134
+
4135
+ # Initialize curve names visibility (default to visible)
4136
+ fig._curve_names_visible = True
4137
+ # Initialize stack label position (default to top/max)
4138
+ fig._stack_label_at_bottom = False
4139
+ fig._label_anchor_left = False
4140
+
4130
4141
  # ---------------- CIF tick overlay (after labels placed) ----------------
4131
4142
  def _ensure_wavelength_for_2theta():
4132
4143
  """Ensure wavelength assigned to all CIF tick sets without prompting.
@@ -46,10 +46,85 @@ Physical meaning:
46
46
  from __future__ import annotations
47
47
 
48
48
  import os
49
+ from typing import Any, Optional
50
+
49
51
  import numpy as np
50
52
 
51
53
 
52
- def convert_xrd_data(filenames, from_param: str, to_param: str):
54
+ def _resolve_readcol_for_file(
55
+ fname: str,
56
+ readcol_by_file: Optional[dict[str, Any]] = None,
57
+ readcol_by_ext: Optional[dict[str, tuple[int, int]]] = None,
58
+ readcol_global: Optional[Any] = None,
59
+ ) -> Optional[tuple[int, int, Optional[int]]]:
60
+ """
61
+ Resolve which columns to use for a file (x_col, y_col, e_col).
62
+ Returns (x_col, y_col, e_col) 1-indexed, or None to use defaults (1, 2, 3).
63
+ e_col is optional; if None, no error column. Uses first pair for multi-curve.
64
+ """
65
+ if not readcol_by_file and not readcol_by_ext and not readcol_global:
66
+ return None
67
+
68
+ # Try per-file match (multiple strategies for cross-platform path matching)
69
+ rc = None
70
+ if readcol_by_file:
71
+ norm_fname = os.path.normpath(fname)
72
+ abs_fname = os.path.abspath(fname)
73
+ base_fname = os.path.basename(fname)
74
+ for key in readcol_by_file:
75
+ key_norm = os.path.normpath(key)
76
+ key_abs = os.path.abspath(key)
77
+ key_base = os.path.basename(key)
78
+ # Prefer exact/normpath/abspath match; use basename only as last resort
79
+ if fname == key or norm_fname == key_norm or abs_fname == key_abs:
80
+ rc = readcol_by_file[key]
81
+ break
82
+ # Basename fallback only if no match and exactly one key has this basename
83
+ if rc is None and base_fname:
84
+ matches = [k for k in readcol_by_file if os.path.basename(k) == base_fname]
85
+ if len(matches) == 1:
86
+ rc = readcol_by_file[matches[0]]
87
+
88
+ # Fall back to per-extension
89
+ if rc is None and readcol_by_ext:
90
+ _, ext = os.path.splitext(fname)
91
+ ext_lower = ext.lower() if ext else ""
92
+ rc = readcol_by_ext.get(ext_lower)
93
+
94
+ # Fall back to global
95
+ if rc is None and readcol_global is not None:
96
+ rc = readcol_global
97
+
98
+ if rc is None:
99
+ return None
100
+
101
+ # Normalize to single (x, y) pair; for multi-curve use first pair
102
+ if isinstance(rc, (list, tuple)) and len(rc) >= 2:
103
+ first = rc[0]
104
+ if isinstance(first, (list, tuple)) and len(first) >= 2:
105
+ x_col, y_col = int(first[0]), int(first[1])
106
+ else:
107
+ x_col, y_col = int(rc[0]), int(rc[1])
108
+ elif isinstance(rc, (list, tuple)) and len(rc) == 1:
109
+ first = rc[0]
110
+ if isinstance(first, (list, tuple)) and len(first) >= 2:
111
+ x_col, y_col = int(first[0]), int(first[1])
112
+ else:
113
+ return None
114
+ else:
115
+ return None
116
+
117
+ # Optional error column: use y_col + 1 if user might have x, y, e layout
118
+ e_col = y_col + 1
119
+ return (x_col, y_col, e_col)
120
+
121
+
122
+ def convert_xrd_data(
123
+ filenames,
124
+ from_param: str,
125
+ to_param: str,
126
+ args: Optional[Any] = None,
127
+ ):
53
128
  """
54
129
  Convert XRD data files between different representations.
55
130
 
@@ -85,6 +160,9 @@ def convert_xrd_data(filenames, from_param: str, to_param: str):
85
160
  filenames: List of file paths to convert (e.g., ['data.xy', 'pattern.xye'])
86
161
  from_param: Source parameter - either a wavelength (float as string) or 'q'
87
162
  to_param: Target parameter - either a wavelength (float as string) or 'q'
163
+ args: Optional parsed args with readcol_by_file, readcol_by_ext, readcol for
164
+ column selection (1-indexed). When provided, uses these instead of
165
+ default columns 1, 2, 3. Compatible with --readcol, --readcolxy, etc.
88
166
 
89
167
  Output:
90
168
  Creates converted files in a 'converted' subfolder within the directory
@@ -137,6 +215,7 @@ def convert_xrd_data(filenames, from_param: str, to_param: str):
137
215
  return
138
216
 
139
217
  # Process each file
218
+ output_dirs = set()
140
219
  for fname in filenames:
141
220
  # Validate file exists
142
221
  if not os.path.isfile(fname):
@@ -158,11 +237,35 @@ def convert_xrd_data(filenames, from_param: str, to_param: str):
158
237
  if data.shape[1] < 2:
159
238
  print(f"Invalid data format in {fname}: need at least 2 columns (x, y)")
160
239
  continue
161
-
162
- # Extract columns
163
- x = data[:, 0] # X values (2θ or Q)
164
- y = data[:, 1] # Intensity values
165
- e = data[:, 2] if data.shape[1] >= 3 else None # Error bars (optional)
240
+
241
+ # Resolve column selection (--readcol, --readcolxy, etc.)
242
+ readcol_by_file = getattr(args, "readcol_by_file", None) or {}
243
+ readcol_by_ext = getattr(args, "readcol_by_ext", None) or {}
244
+ readcol_global = getattr(args, "readcol", None)
245
+ resolved = _resolve_readcol_for_file(
246
+ fname, readcol_by_file, readcol_by_ext, readcol_global
247
+ )
248
+
249
+ if resolved:
250
+ x_col, y_col, e_col = resolved
251
+ # Convert 1-indexed to 0-indexed
252
+ x_idx, y_idx = x_col - 1, y_col - 1
253
+ if x_idx < 0 or y_idx < 0 or x_idx >= data.shape[1] or y_idx >= data.shape[1]:
254
+ print(f"Error in {fname}: --readcol columns {x_col}, {y_col} out of range (file has {data.shape[1]} columns)")
255
+ continue
256
+ x = data[:, x_idx]
257
+ y = data[:, y_idx]
258
+ # Use error column if specified and exists
259
+ if e_col is not None and e_col <= data.shape[1]:
260
+ e_idx = e_col - 1
261
+ e = data[:, e_idx] if e_idx >= 0 else None
262
+ else:
263
+ e = None
264
+ else:
265
+ # Default: columns 0, 1, 2 (1-indexed: 1, 2, 3)
266
+ x = data[:, 0]
267
+ y = data[:, 1]
268
+ e = data[:, 2] if data.shape[1] >= 3 else None
166
269
 
167
270
  # Perform conversion based on type
168
271
  if conversion_type == "wavelength_to_wavelength":
@@ -227,9 +330,14 @@ def convert_xrd_data(filenames, from_param: str, to_param: str):
227
330
  # Use UTF-8 encoding to support Greek letters (θ) in headers on all platforms
228
331
  np.savetxt(output_fname, out_data, fmt="% .6f", header=header, encoding='utf-8')
229
332
  print(f"Saved {output_fname}")
333
+ output_dirs.add(output_dir)
230
334
  except Exception as e:
231
335
  print(f"Error saving {output_fname}: {e}")
232
336
 
337
+ if output_dirs:
338
+ dirs_str = ", ".join(sorted(output_dirs))
339
+ print(f"Exported to: {dirs_str}")
340
+
233
341
 
234
342
  def convert_to_qye(filenames, wavelength: float):
235
343
  """
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.29] - 2026-03-10
4
+ - Improved compatibility of `--convert` with `--readcol`: conversion now respects per-file, per-extension, and global column selection
5
+ - Use custom columns when converting XRD data: `batplot data.csv --readcol 3 4 --convert 1.54 q`
6
+ - Per-file columns for convert: `batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q`
7
+ - Fixed `--all style.bps --readcol 2 3`: style files (.bps, .bpsg, .bpcfg) no longer steal readcol; global readcol now applies to all data files
8
+
3
9
  ## [1.8.16] - 2026-03-09
4
10
  - GC and dQdV modes now support multiple files
5
11
 
@@ -231,6 +231,21 @@ All converted files are saved in a `converted` subfolder within the directory co
231
231
  - Q-space files: `.qye` extension
232
232
  - 2θ files: `.xy` or original extension
233
233
 
234
+ ### Custom columns with --readcol
235
+
236
+ When your file has 2θ or Q in non-standard columns, use `--readcol` with `--convert`:
237
+
238
+ ```bash
239
+ # 2θ in column 3, intensity in column 4
240
+ batplot data.csv --readcol 3 4 --convert 1.54 q
241
+
242
+ # Per-file columns
243
+ batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q
244
+
245
+ # Extension-specific: --readcoldat, --readcolcsv, etc.
246
+ batplot file.dat --readcoldat 2 4 --convert 1.54 q
247
+ ```
248
+
234
249
  ### Examples
235
250
 
236
251
  ```bash
@@ -73,6 +73,18 @@ def update_labels(ax, y_data_list: List, label_text_objects: List, stack_mode: b
73
73
  fig = getattr(ax, 'figure', None)
74
74
  label_anchor_left = bool(getattr(fig, '_label_anchor_left', False)) if fig is not None else False
75
75
 
76
+ # Line lookup for dual y-axis (--ry): curve index -> Line2D (ax or ax2)
77
+ # When --ry is used, curves are split between ax and ax2; _xy_lines_by_curve maps correctly.
78
+ def _line_for_curve(i):
79
+ lines_src = getattr(fig, '_xy_lines_by_curve', None) if fig else None
80
+ if lines_src is not None and 0 <= i < len(lines_src):
81
+ ln = lines_src[i]
82
+ if ln is not None:
83
+ return ln
84
+ if i < len(ax.lines):
85
+ return ax.lines[i]
86
+ return None
87
+
76
88
  # ====================================================================
77
89
  if stack_mode:
78
90
  # Get plot edges in data coordinates
@@ -118,9 +130,9 @@ def update_labels(ax, y_data_list: List, label_text_objects: List, stack_mode: b
118
130
 
119
131
  # Set label color to match curve color (makes identification easier)
120
132
  try:
121
- if i < len(ax.lines):
122
- # Get color from corresponding line object
123
- txt.set_color(ax.lines[i].get_color())
133
+ ln = _line_for_curve(i)
134
+ if ln is not None:
135
+ txt.set_color(ln.get_color())
124
136
  except Exception:
125
137
  # If color matching fails, keep default color
126
138
  pass
@@ -182,8 +194,9 @@ def update_labels(ax, y_data_list: List, label_text_objects: List, stack_mode: b
182
194
 
183
195
  # Match label color to curve color
184
196
  try:
185
- if i < len(ax.lines):
186
- txt.set_color(ax.lines[i].get_color())
197
+ ln = _line_for_curve(i)
198
+ if ln is not None:
199
+ txt.set_color(ln.get_color())
187
200
  except Exception:
188
201
  pass
189
202
  else:
@@ -213,8 +226,9 @@ def update_labels(ax, y_data_list: List, label_text_objects: List, stack_mode: b
213
226
 
214
227
  # Match label color to curve color
215
228
  try:
216
- if i < len(ax.lines):
217
- txt.set_color(ax.lines[i].get_color())
229
+ ln = _line_for_curve(i)
230
+ if ln is not None:
231
+ txt.set_color(ln.get_color())
218
232
  except Exception:
219
233
  pass
220
234
 
@@ -101,10 +101,13 @@ def _wrap_line(text: str, width: int) -> List[str]:
101
101
  UPDATE_INFO = {
102
102
  # Custom message to include in update notification
103
103
  # (Auto-filled from RELEASE_NOTES.txt when using batplot --dev-upgrade)
104
- 'custom_message': '- Bug fixes',
104
+ 'custom_message': '- Improved compatibility of --convert with --readcol: conversion now respects per-file, per-extension, and global column selection',
105
105
  # Additional notes (auto-filled from RELEASE_NOTES.txt)
106
106
  'update_notes': [
107
- '- Bug fixes'
107
+ '- Improved compatibility of --convert with --readcol: conversion now respects per-file, per-extension, and global column selection',
108
+ '- Use custom columns when converting XRD data: batplot data.csv --readcol 3 4 --convert 1.54 q',
109
+ '- Per-file columns for convert: batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q',
110
+ '- Fixed --all style.bps --readcol 2 3: style files no longer steal readcol; global readcol applies to all data files'
108
111
  ],
109
112
  'show_update_notes': True,
110
113
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.28
3
+ Version: 1.8.29
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
@@ -112,6 +112,10 @@ batplot scan1.brml:1.5406 scan2.xye:0.7093
112
112
  # Convert and export to converted/ subfolder (q and Q equivalent)
113
113
  batplot data.xye --convert 1.54 q
114
114
  batplot data.qye --convert q 1.54
115
+
116
+ # With --readcol for custom column layout (e.g. 2θ in col 3, intensity in col 4)
117
+ batplot data.csv --readcol 3 4 --convert 1.54 q
118
+ batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q
115
119
  ```
116
120
 
117
121
  ### Stacking and normalization
@@ -152,6 +156,9 @@ batplot data.xy --readcol 1 2 1 3
152
156
 
153
157
  # Range: col 1 as x, cols 2–20 as 19 y-curves
154
158
  batplot file.txt --readcol 1 2-20
159
+
160
+ # With --convert: use custom columns when converting XRD data
161
+ batplot data.csv --readcol 3 4 --convert 1.54 q
155
162
  ```
156
163
 
157
164
  ### Derivatives and EXAFS
@@ -186,6 +193,8 @@ batplot allfiles --xaxis 2theta --xrange 15 75 --interactive
186
193
 
187
194
  ### Galvanostatic cycling (GC)
188
195
 
196
+ GC mode plots potential vs. capacity for each charge/discharge cycle—the primary visualization for battery cycling data. Batplot automatically detects cycles from Neware `.csv` or Biologic `.mpt` files, assigns each cycle a distinct color, and supports both specific capacity (mAh/g) and raw capacity. For `.mpt` files, pass `--mass` (mg) to compute specific capacity. Discontinuous or paused experiments are handled by splitting data into contiguous charge/discharge segments. Use the interactive menu to customize colors, visibility, and export.
197
+
189
198
  ```bash
190
199
  # From .csv (capacity in file)
191
200
  batplot battery.csv --gc
@@ -278,13 +287,17 @@ batplot file1.xye file2.qye structure.cif:1.54 --stack --interactive
278
287
 
279
288
  ## Batch export (--all)
280
289
 
281
- Export each file as a separate figure to `batplot_svg/`:
290
+ Export each file as a separate figure to `Figures/`:
282
291
 
283
292
  ```bash
284
293
  batplot --all
285
294
  batplot --all --format png
286
295
  batplot --all --xaxis 2theta --xrange 10 80
287
296
  batplot --all style.bps --gc --mass 7
297
+
298
+ # With --readcol for custom columns (put --readcol before style file)
299
+ batplot --all --readcol 2 3 --xaxis 2theta
300
+ batplot --all --readcol 2 3 style.bps --xaxis 2theta
288
301
  ```
289
302
 
290
303
  ---
@@ -2,7 +2,6 @@ LICENSE
2
2
  MANIFEST.in
3
3
  NOTICE
4
4
  README.md
5
- USER_MANUAL.md
6
5
  pyproject.toml
7
6
  setup.py
8
7
  batplot/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "batplot"
7
- version = "1.8.28"
7
+ version = "1.8.29"
8
8
  description = "Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing"
9
9
  authors = [
10
10
  { name = "Tian Dai", email = "tianda@uio.no" }