batplot 1.8.31__tar.gz → 1.8.34__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 (42) hide show
  1. {batplot-1.8.31/batplot.egg-info → batplot-1.8.34}/PKG-INFO +2 -1
  2. {batplot-1.8.31 → batplot-1.8.34}/README.md +1 -0
  3. {batplot-1.8.31 → batplot-1.8.34}/batplot/__init__.py +1 -1
  4. {batplot-1.8.31 → batplot-1.8.34}/batplot/args.py +10 -0
  5. {batplot-1.8.31 → batplot-1.8.34}/batplot/batplot.py +189 -0
  6. batplot-1.8.34/batplot/canvas_interactive.py +1189 -0
  7. {batplot-1.8.31 → batplot-1.8.34}/batplot/cpc_interactive.py +15 -14
  8. {batplot-1.8.31 → batplot-1.8.34}/batplot/data/CHANGELOG.md +16 -0
  9. {batplot-1.8.31 → batplot-1.8.34}/batplot/electrochem_interactive.py +475 -230
  10. {batplot-1.8.31 → batplot-1.8.34}/batplot/interactive.py +9 -13
  11. {batplot-1.8.31 → batplot-1.8.34}/batplot/operando_ec_interactive.py +517 -578
  12. {batplot-1.8.31 → batplot-1.8.34}/batplot/session.py +796 -44
  13. batplot-1.8.34/batplot/showcol.py +468 -0
  14. {batplot-1.8.31 → batplot-1.8.34}/batplot/style.py +5 -5
  15. {batplot-1.8.31 → batplot-1.8.34}/batplot/ui.py +7 -13
  16. {batplot-1.8.31 → batplot-1.8.34}/batplot/utils.py +32 -7
  17. {batplot-1.8.31 → batplot-1.8.34}/batplot/version_check.py +2 -2
  18. {batplot-1.8.31 → batplot-1.8.34/batplot.egg-info}/PKG-INFO +2 -1
  19. {batplot-1.8.31 → batplot-1.8.34}/batplot.egg-info/SOURCES.txt +2 -0
  20. {batplot-1.8.31 → batplot-1.8.34}/pyproject.toml +1 -1
  21. {batplot-1.8.31 → batplot-1.8.34}/LICENSE +0 -0
  22. {batplot-1.8.31 → batplot-1.8.34}/MANIFEST.in +0 -0
  23. {batplot-1.8.31 → batplot-1.8.34}/NOTICE +0 -0
  24. {batplot-1.8.31 → batplot-1.8.34}/batplot/batch.py +0 -0
  25. {batplot-1.8.31 → batplot-1.8.34}/batplot/cif.py +0 -0
  26. {batplot-1.8.31 → batplot-1.8.34}/batplot/cli.py +0 -0
  27. {batplot-1.8.31 → batplot-1.8.34}/batplot/color_utils.py +0 -0
  28. {batplot-1.8.31 → batplot-1.8.34}/batplot/config.py +0 -0
  29. {batplot-1.8.31 → batplot-1.8.34}/batplot/converters.py +0 -0
  30. {batplot-1.8.31 → batplot-1.8.34}/batplot/data/USER_MANUAL.md +0 -0
  31. {batplot-1.8.31 → batplot-1.8.34}/batplot/dev_upgrade.py +0 -0
  32. {batplot-1.8.31 → batplot-1.8.34}/batplot/manual.py +0 -0
  33. {batplot-1.8.31 → batplot-1.8.34}/batplot/modes.py +0 -0
  34. {batplot-1.8.31 → batplot-1.8.34}/batplot/operando.py +0 -0
  35. {batplot-1.8.31 → batplot-1.8.34}/batplot/plotting.py +0 -0
  36. {batplot-1.8.31 → batplot-1.8.34}/batplot/readers.py +0 -0
  37. {batplot-1.8.31 → batplot-1.8.34}/batplot.egg-info/dependency_links.txt +0 -0
  38. {batplot-1.8.31 → batplot-1.8.34}/batplot.egg-info/entry_points.txt +0 -0
  39. {batplot-1.8.31 → batplot-1.8.34}/batplot.egg-info/requires.txt +0 -0
  40. {batplot-1.8.31 → batplot-1.8.34}/batplot.egg-info/top_level.txt +0 -0
  41. {batplot-1.8.31 → batplot-1.8.34}/setup.cfg +0 -0
  42. {batplot-1.8.31 → batplot-1.8.34}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.31
3
+ Version: 1.8.34
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
@@ -65,6 +65,7 @@ With a single line of command to easily plot publication-ready plots with custom
65
65
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
66
66
  - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
67
67
  - **Batch Processing**: Export each file separately with `--all`
68
+ - **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.)
68
69
 
69
70
  ## Installation
70
71
 
@@ -14,6 +14,7 @@ With a single line of command to easily plot publication-ready plots with custom
14
14
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
15
15
  - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
16
16
  - **Batch Processing**: Export each file separately with `--all`
17
+ - **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.)
17
18
 
18
19
  ## Installation
19
20
 
@@ -1,5 +1,5 @@
1
1
  """batplot: Interactive plotting for battery data visualization."""
2
2
 
3
- __version__ = "1.8.31"
3
+ __version__ = "1.8.34"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -168,6 +168,7 @@ def _print_general_help() -> None:
168
168
 
169
169
  "More help:\n"
170
170
  " batplot --version # Version and release info (with option to show full release notes)\n"
171
+ " batplot --showcol FILE [FILE...] # Preview column names + first 10 values per column\n"
171
172
  " batplot --help # This help\n"
172
173
  " batplot --help xy # XY file plotting guide\n"
173
174
  " batplot --help ec # Electrochemistry (GC/dQdV/CV/CPC) guide\n"
@@ -231,6 +232,8 @@ def _print_xy_help() -> None:
231
232
  " Works with --readcol for custom column layout (per-file, per-ext, or global):\n"
232
233
  " batplot data.csv --readcol 3 4 --convert 1.54 q\n"
233
234
  " batplot f1.txt --readcol 2 3 f2.txt --readcol 5 6 --convert 1.54 q\n"
235
+ " Directory: pass a folder to convert all .xy/.xye/.qye/.dat/.csv/.txt files:\n"
236
+ " batplot /path/to/folder --convert 0.25448 1.54\n"
234
237
  " Examples:\n"
235
238
  " batplot file.xye --convert 1.54 0.25\n"
236
239
  " batplot file.xye --convert 1.54 q\n"
@@ -415,6 +418,11 @@ def build_parser() -> argparse.ArgumentParser:
415
418
  help=argparse.SUPPRESS) # SUPPRESS hides from auto-generated help
416
419
  parser.add_argument("--version", action="store_true", dest="version",
417
420
  help="Show version and current release info, then exit.")
421
+ parser.add_argument(
422
+ "--showcol",
423
+ action="store_true",
424
+ help=argparse.SUPPRESS,
425
+ )
418
426
  parser.add_argument("--manual", action="store_true", help=argparse.SUPPRESS)
419
427
 
420
428
  # ====================================================================
@@ -491,6 +499,8 @@ def build_parser() -> argparse.ArgumentParser:
491
499
  help=argparse.SUPPRESS)
492
500
  parser.add_argument("--1d", action="store_true", dest="derivative_1d", help=argparse.SUPPRESS)
493
501
  parser.add_argument("--2d", action="store_true", dest="derivative_2d", help=argparse.SUPPRESS)
502
+ parser.add_argument("--canvas", action="store_true", dest="canvas",
503
+ help="Canvas mode: combine multiple .pkl sessions into one layout. Use numbers to edit each panel.")
494
504
  return parser
495
505
 
496
506
 
@@ -292,6 +292,143 @@ def _handle_cv_mode(args) -> int:
292
292
  # Multiple files: create output directory
293
293
  out_dir = ensure_subdirectory('Figures', os.getcwd())
294
294
 
295
+ # CV multi-file combined mode: one figure with all files overlaid (like GC/dQ/dV)
296
+ if len(data_files) > 1:
297
+ plt.rcParams.update({
298
+ 'font.family': 'sans-serif',
299
+ 'font.sans-serif': ['DejaVu Sans', 'Arial', 'Helvetica', 'STIXGeneral', 'Liberation Sans', 'Arial Unicode MS'],
300
+ 'mathtext.fontset': 'dejavusans',
301
+ 'font.size': 16
302
+ })
303
+ fig, ax = plt.subplots(figsize=(10, 6))
304
+ file_data = []
305
+ base_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
306
+ '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
307
+ for file_idx, ec_file in enumerate(data_files):
308
+ if not os.path.isfile(ec_file) or not (ec_file.lower().endswith('.mpt') or ec_file.lower().endswith('.txt')):
309
+ continue
310
+ try:
311
+ if ec_file.lower().endswith('.txt'):
312
+ voltage, current, cycles = read_biologic_txt_file(ec_file, mode='cv')
313
+ else:
314
+ mpt_result = read_mpt_file(ec_file, mode='cv')
315
+ voltage = mpt_result[0]
316
+ current = mpt_result[1]
317
+ cycles = mpt_result[2]
318
+ cyc_int_raw = np.array(np.rint(cycles), dtype=int)
319
+ if cyc_int_raw.size:
320
+ unique_cycles_raw = np.unique(cyc_int_raw)
321
+ valid_min_c = None
322
+ for c in sorted(unique_cycles_raw):
323
+ if np.sum(cyc_int_raw == c) >= 2:
324
+ valid_min_c = int(c)
325
+ break
326
+ if valid_min_c is not None:
327
+ shift = 1 - valid_min_c
328
+ else:
329
+ min_c = int(np.min(cyc_int_raw))
330
+ shift = 1 - min_c if min_c <= 0 else 0
331
+ else:
332
+ shift = 0
333
+ cyc_int = cyc_int_raw + shift
334
+ cycles_present = sorted(int(c) for c in np.unique(cyc_int)) if cyc_int.size else [1]
335
+ color = base_colors[file_idx % len(base_colors)]
336
+ cycle_lines = {}
337
+ file_lbl = os.path.basename(ec_file)
338
+ for cyc in cycles_present:
339
+ mask = (cyc_int == cyc)
340
+ idx = np.where(mask)[0]
341
+ if idx.size >= 2:
342
+ parts_x, parts_y = [], []
343
+ start = 0
344
+ for k in range(1, idx.size):
345
+ if idx[k] != idx[k - 1] + 1:
346
+ parts_x.append(voltage[idx[start:k]])
347
+ parts_y.append(current[idx[start:k]])
348
+ start = k
349
+ parts_x.append(voltage[idx[start:]])
350
+ parts_y.append(current[idx[start:]])
351
+ X, Y = [], []
352
+ for i, (px, py) in enumerate(zip(parts_x, parts_y)):
353
+ if i > 0:
354
+ X.append(np.array([np.nan]))
355
+ Y.append(np.array([np.nan]))
356
+ X.append(px)
357
+ Y.append(py)
358
+ x_b = np.concatenate(X) if X else np.array([])
359
+ y_b = np.concatenate(Y) if Y else np.array([])
360
+ label = f"{file_lbl}: {cyc}" if file_lbl else str(cyc)
361
+ if getattr(args, 'ro', False):
362
+ ln, = ax.plot(y_b, x_b, '-', color=color, linewidth=2.0, label=label, alpha=0.8)
363
+ else:
364
+ ln, = ax.plot(x_b, y_b, '-', color=color, linewidth=2.0, label=label, alpha=0.8)
365
+ cycle_lines[cyc] = ln
366
+ file_data.append({
367
+ 'filename': os.path.basename(ec_file),
368
+ 'display_name': os.path.basename(ec_file),
369
+ 'cycle_lines': cycle_lines,
370
+ 'visible': True,
371
+ 'filepath': ec_file,
372
+ })
373
+ except Exception as _e:
374
+ print(f"CV multi-file: skip {ec_file}: {_e}")
375
+ if not file_data:
376
+ print("CV multi-file: no files loaded.")
377
+ return 1
378
+ if getattr(args, 'ro', False):
379
+ ax.set_xlabel('Current (mA)', labelpad=8.0)
380
+ ax.set_ylabel('Potential (V)', labelpad=8.0)
381
+ else:
382
+ ax.set_xlabel('Potential (V)', labelpad=8.0)
383
+ ax.set_ylabel('Current (mA)', labelpad=8.0)
384
+ ax.legend(title='Cycle')
385
+ fig._ec_legend_title = "Cycle"
386
+ fig.subplots_adjust(left=0.12, right=0.95, top=0.88, bottom=0.15)
387
+ if style_cfg:
388
+ try:
389
+ _apply_ec_style(fig, ax, style_cfg)
390
+ if hasattr(fig, 'canvas'):
391
+ fig.canvas.draw()
392
+ except Exception as e:
393
+ print(f"Warning: Error applying style file: {e}")
394
+ try:
395
+ plt.ion()
396
+ except Exception:
397
+ pass
398
+ plt.show(block=False)
399
+ try:
400
+ fig._ro_active = bool(getattr(args, "ro", False))
401
+ except Exception:
402
+ pass
403
+ try:
404
+ fig._bp_source_paths = [os.path.abspath(f.get('filepath', '')) for f in file_data if f.get('filepath')]
405
+ except Exception:
406
+ pass
407
+ if args.interactive:
408
+ try:
409
+ electrochem_interactive_menu(fig, ax, file_data=file_data)
410
+ except Exception as _ie:
411
+ print(f"Interactive menu failed: {_ie}")
412
+ plt.show()
413
+ else:
414
+ if out_dir and (args.savefig or getattr(args, 'out', None)):
415
+ out_path = getattr(args, 'out', None)
416
+ if not out_path:
417
+ ext = getattr(args, 'format', 'svg') or 'svg'
418
+ out_path = os.path.join(out_dir, f'CV_combined.{ext}')
419
+ try:
420
+ fig.savefig(out_path, dpi=300, bbox_inches='tight')
421
+ print(f"Saved {out_path}")
422
+ except Exception as e:
423
+ print(f"Could not save: {e}")
424
+ try:
425
+ plt.ioff()
426
+ except Exception:
427
+ pass
428
+ print(f"Processed {len(file_data)} CV files.")
429
+ plt.show(block=True)
430
+ return 0
431
+
295
432
  for ec_file in data_files:
296
433
  if not os.path.isfile(ec_file):
297
434
  print(f"File not found: {ec_file}")
@@ -531,6 +668,15 @@ def batplot_main() -> int: # type: ignore
531
668
  print(f"batplot v{__version__}")
532
669
  return 0
533
670
 
671
+ # --showcol: print column names (if any) and first data rows, then exit
672
+ if getattr(args, "showcol", False):
673
+ if not args.files:
674
+ print("batplot --showcol: provide at least one data file.")
675
+ return 1
676
+ from .showcol import run_showcol
677
+
678
+ return run_showcol(args.files)
679
+
534
680
  # ====================================================================
535
681
  # STEP 2: VALIDATE INPUT
536
682
  # ====================================================================
@@ -2457,6 +2603,33 @@ def batplot_main() -> int: # type: ignore
2457
2603
 
2458
2604
  _maybe_expand_allfiles_argument(args, ec_mode_active)
2459
2605
 
2606
+ # ---------------- Handle --convert (before batch/sole logic) ----------------
2607
+ # When --convert is used with a directory, expand to all XY files in that directory
2608
+ if args.convert:
2609
+ if not args.files:
2610
+ print("Error: --convert requires file(s) or a directory to convert")
2611
+ exit(1)
2612
+ from .utils import natural_sort_key
2613
+ convert_ext = {'.xy', '.xye', '.qye', '.dat', '.csv', '.txt'}
2614
+ expanded = []
2615
+ for p in args.files:
2616
+ if os.path.isfile(p):
2617
+ expanded.append(p)
2618
+ elif os.path.isdir(p):
2619
+ for f in sorted(os.listdir(p), key=natural_sort_key):
2620
+ fp = os.path.join(p, f)
2621
+ if os.path.isfile(fp) and os.path.splitext(f)[1].lower() in convert_ext:
2622
+ expanded.append(fp)
2623
+ else:
2624
+ print(f"Warning: Not a file or directory: {p}")
2625
+ if expanded:
2626
+ from_param, to_param = args.convert
2627
+ convert_xrd_data(expanded, from_param, to_param, args=args)
2628
+ else:
2629
+ print("Error: No convertible files found (.xy, .xye, .qye, .dat, .csv, .txt)")
2630
+ exit(1)
2631
+ exit()
2632
+
2460
2633
  if len(args.files) == 1:
2461
2634
  sole = args.files[0]
2462
2635
  if sole.lower() == 'all':
@@ -2474,6 +2647,22 @@ def batplot_main() -> int: # type: ignore
2474
2647
  if not ec_mode_active and getattr(args, 'all', None) is not None:
2475
2648
  batch_process(os.getcwd(), args)
2476
2649
  exit()
2650
+
2651
+ # ---------------- Canvas mode: combine multiple .pkl sessions ----------------
2652
+ if getattr(args, 'canvas', False) and args.files:
2653
+ if all(f.lower().endswith('.pkl') for f in args.files):
2654
+ try:
2655
+ from .canvas_interactive import run_canvas_mode
2656
+ run_canvas_mode(args.files)
2657
+ except Exception as e:
2658
+ print(f"Canvas mode failed: {e}")
2659
+ import traceback
2660
+ traceback.print_exc()
2661
+ exit(1)
2662
+ exit()
2663
+ else:
2664
+ print("Canvas mode requires all files to be .pkl session files.")
2665
+ exit(1)
2477
2666
 
2478
2667
  # ---------------- Normal (multi-file) path continues below ----------------
2479
2668
  # Apply conditional default for delta (normal mode only)