batplot 1.0.6__tar.gz → 1.0.7__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.0.6 → batplot-1.0.7}/PKG-INFO +2 -2
- {batplot-1.0.6 → batplot-1.0.7}/batplot/batplot.py +127 -65
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/PKG-INFO +2 -2
- {batplot-1.0.6 → batplot-1.0.7}/pyproject.toml +2 -2
- {batplot-1.0.6 → batplot-1.0.7}/batplot/__init__.py +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/SOURCES.txt +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/batplot.egg-info/top_level.txt +0 -0
- {batplot-1.0.6 → batplot-1.0.7}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.7
|
|
4
4
|
Summary: Interactive plotting for XRD, PDF, and XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir)
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Tian Dai <tianda@uio.no>
|
|
6
6
|
Requires-Python: >=3.7
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: numpy
|
|
@@ -649,19 +649,19 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
649
649
|
# REPLACED print_main_menu with column layout (now hides 'd' and 'y' in --stack)
|
|
650
650
|
is_diffraction = use_Q or (not use_r and not use_E and not use_k and not use_rft) # 2θ or Q
|
|
651
651
|
def print_main_menu():
|
|
652
|
-
|
|
652
|
+
has_cif = False
|
|
653
653
|
try:
|
|
654
|
-
|
|
655
|
-
col1.append("z: hkl")
|
|
654
|
+
has_cif = any(f.lower().endswith('.cif') for f in args.files)
|
|
656
655
|
except Exception:
|
|
657
656
|
pass
|
|
657
|
+
col1 = ["c: colors", "f: font", "l: line", "t: ticks"]
|
|
658
|
+
if has_cif:
|
|
659
|
+
col1.append("z: hkl")
|
|
658
660
|
col2 = ["a: rearrange", "d: offset", "r: rename", "g: size","x: change X", "y: change Y"]
|
|
659
|
-
# Added s: save (session). 'o: open' removed; load .pkl by invoking batplot file.pkl
|
|
660
661
|
col3 = ["v: find peaks", "p: print style", "i: import style", "n: crosshair", "e: export", "s: save", "b: undo", "q: quit"]
|
|
661
662
|
if args.stack:
|
|
662
663
|
col2 = [item for item in col2 if not item.startswith("d:") and not item.startswith("y:")]
|
|
663
664
|
if not is_diffraction:
|
|
664
|
-
# Remove crosshair option in non-diffraction modes
|
|
665
665
|
col3 = [item for item in col3 if not item.startswith("n:")]
|
|
666
666
|
rows = max(len(col1), len(col2), len(col3))
|
|
667
667
|
print("\nInteractive menu:")
|
|
@@ -671,6 +671,17 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
671
671
|
p2 = col2[i] if i < len(col2) else ""
|
|
672
672
|
p3 = col3[i] if i < len(col3) else ""
|
|
673
673
|
print(f" {p1:<16} {p2:<16} {p3:<16}")
|
|
674
|
+
|
|
675
|
+
# --- Helper for spine visibility ---
|
|
676
|
+
def set_spine_visible(which, visible):
|
|
677
|
+
if which in ax.spines:
|
|
678
|
+
ax.spines[which].set_visible(visible)
|
|
679
|
+
fig.canvas.draw_idle()
|
|
680
|
+
|
|
681
|
+
def get_spine_visible(which):
|
|
682
|
+
if which in ax.spines:
|
|
683
|
+
return ax.spines[which].get_visible()
|
|
684
|
+
return False
|
|
674
685
|
# Initial menu display REMOVED to avoid double print
|
|
675
686
|
# print_main_menu()
|
|
676
687
|
ax.set_aspect('auto', adjustable='datalim')
|
|
@@ -727,6 +738,20 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
727
738
|
if new_family:
|
|
728
739
|
axis_label.set_fontfamily(new_family)
|
|
729
740
|
|
|
741
|
+
# Top duplicate label
|
|
742
|
+
if hasattr(ax, '_top_xlabel_artist') and ax._top_xlabel_artist is not None:
|
|
743
|
+
if new_size is not None:
|
|
744
|
+
ax._top_xlabel_artist.set_fontsize(new_size)
|
|
745
|
+
if new_family:
|
|
746
|
+
ax._top_xlabel_artist.set_fontfamily(new_family)
|
|
747
|
+
|
|
748
|
+
# Right duplicate manual label
|
|
749
|
+
if hasattr(ax, '_right_ylabel_artist') and ax._right_ylabel_artist is not None:
|
|
750
|
+
if new_size is not None:
|
|
751
|
+
ax._right_ylabel_artist.set_fontsize(new_size)
|
|
752
|
+
if new_family:
|
|
753
|
+
ax._right_ylabel_artist.set_fontfamily(new_family)
|
|
754
|
+
|
|
730
755
|
# Tick labels
|
|
731
756
|
for lbl in ax.get_xticklabels() + ax.get_yticklabels():
|
|
732
757
|
if new_size is not None:
|
|
@@ -852,32 +877,6 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
852
877
|
if x == BIRD_X and y == bird_y:
|
|
853
878
|
ch = "@"
|
|
854
879
|
# obstacle
|
|
855
|
-
else:
|
|
856
|
-
for o in obstacles:
|
|
857
|
-
if o.x == x:
|
|
858
|
-
if not (o.gap_start <= y < o.gap_start + GAP_SIZE):
|
|
859
|
-
ch = "#"
|
|
860
|
-
break
|
|
861
|
-
row_chars.append(ch)
|
|
862
|
-
print("|" + "".join(row_chars) + "|")
|
|
863
|
-
print(top_border)
|
|
864
|
-
print(f"Tick: {tick} Score: {score} (j=jump, Enter=fall, q=quit)")
|
|
865
|
-
|
|
866
|
-
def collision():
|
|
867
|
-
# ground / ceiling
|
|
868
|
-
if bird_y < 0 or bird_y >= HEIGHT:
|
|
869
|
-
return True
|
|
870
|
-
for o in obstacles:
|
|
871
|
-
if o.x == BIRD_X:
|
|
872
|
-
if not (o.gap_start <= bird_y < o.gap_start + GAP_SIZE):
|
|
873
|
-
return True
|
|
874
|
-
return False
|
|
875
|
-
|
|
876
|
-
print("\n=== Jumping Bird ===")
|
|
877
|
-
print("Looks like you entered a wrong command!")
|
|
878
|
-
print("Goal: pass pillars (#). Controls each turn:")
|
|
879
|
-
print(" j + Enter -> jump")
|
|
880
|
-
print(" Enter -> fall")
|
|
881
880
|
print(" q -> quit game")
|
|
882
881
|
print("Bird = @ | Score increments when you pass a pillar.\n")
|
|
883
882
|
|
|
@@ -1402,6 +1401,8 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1402
1401
|
if line.get_visible():
|
|
1403
1402
|
return line.get_linewidth()
|
|
1404
1403
|
return None
|
|
1404
|
+
# Save spine visibility
|
|
1405
|
+
spine_vis = {name: sp.get_visible() for name, sp in ax.spines.items()}
|
|
1405
1406
|
|
|
1406
1407
|
bbox = ax.get_position()
|
|
1407
1408
|
frame_w_in = bbox.width * fw
|
|
@@ -1439,7 +1440,8 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1439
1440
|
"spines": {
|
|
1440
1441
|
name: {
|
|
1441
1442
|
"linewidth": sp.get_linewidth(),
|
|
1442
|
-
"color": sp.get_edgecolor()
|
|
1443
|
+
"color": sp.get_edgecolor(),
|
|
1444
|
+
"visible": spine_vis.get(name, True)
|
|
1443
1445
|
} for name, sp in ax.spines.items()
|
|
1444
1446
|
},
|
|
1445
1447
|
"lines": [
|
|
@@ -1621,6 +1623,8 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1621
1623
|
ax.spines[name].set_edgecolor(sp_dict["color"])
|
|
1622
1624
|
except Exception:
|
|
1623
1625
|
pass
|
|
1626
|
+
if "visible" in sp_dict:
|
|
1627
|
+
ax.spines[name].set_visible(sp_dict["visible"])
|
|
1624
1628
|
|
|
1625
1629
|
# ---- Lines (full style) ----
|
|
1626
1630
|
for entry in cfg.get("lines", []):
|
|
@@ -1840,6 +1844,8 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1840
1844
|
fw, fh = fig.get_size_inches()
|
|
1841
1845
|
frame_w_in = bbox.width * fw
|
|
1842
1846
|
frame_h_in = bbox.height * fh
|
|
1847
|
+
# Save spine visibility
|
|
1848
|
+
spine_vis = {name: sp.get_visible() for name, sp in ax.spines.items()}
|
|
1843
1849
|
sess = {
|
|
1844
1850
|
'version': SESSION_VERSION,
|
|
1845
1851
|
'x_data': [np.array(a) for a in x_data_list],
|
|
@@ -1877,7 +1883,8 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1877
1883
|
'bottom': float(bbox.y0),
|
|
1878
1884
|
'right': float(bbox.x0 + bbox.width),
|
|
1879
1885
|
'top': float(bbox.y0 + bbox.height)
|
|
1880
|
-
}
|
|
1886
|
+
},
|
|
1887
|
+
'spine_vis': spine_vis
|
|
1881
1888
|
},
|
|
1882
1889
|
'tick_state': tick_state.copy(),
|
|
1883
1890
|
'font': {
|
|
@@ -1990,6 +1997,11 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
1990
1997
|
pass
|
|
1991
1998
|
# Restore figure size & dpi if present
|
|
1992
1999
|
fig_cfg = sess.get('figure', {})
|
|
2000
|
+
# Restore spine visibility if present
|
|
2001
|
+
spine_vis = fig_cfg.get('spine_vis', {})
|
|
2002
|
+
for name, vis in spine_vis.items():
|
|
2003
|
+
if name in ax.spines:
|
|
2004
|
+
ax.spines[name].set_visible(vis)
|
|
1993
2005
|
try:
|
|
1994
2006
|
if fig_cfg.get('size') and isinstance(fig_cfg['size'], (list, tuple)) and len(fig_cfg['size']) == 2:
|
|
1995
2007
|
fw, fh = fig_cfg['size']
|
|
@@ -2357,11 +2369,16 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2357
2369
|
play_jump_game(); continue
|
|
2358
2370
|
elif key == 'c':
|
|
2359
2371
|
try:
|
|
2372
|
+
has_cif = False
|
|
2373
|
+
try:
|
|
2374
|
+
has_cif = any(f.lower().endswith('.cif') for f in args.files)
|
|
2375
|
+
except Exception:
|
|
2376
|
+
pass
|
|
2360
2377
|
while True:
|
|
2361
2378
|
print("Color menu:")
|
|
2362
2379
|
print(" m : manual color mapping (e.g., 1:red 2:#00B006)")
|
|
2363
2380
|
print(" p : apply colormap palette to a range (e.g., 1-3 viridis)")
|
|
2364
|
-
if cif_tick_series:
|
|
2381
|
+
if has_cif and cif_tick_series:
|
|
2365
2382
|
print(" t : change CIF tick set color (e.g., 1:red 2:#888888)")
|
|
2366
2383
|
print(" q : return to main menu")
|
|
2367
2384
|
sub = input("Choose (m/p/t/q): ").strip().lower()
|
|
@@ -2393,7 +2410,7 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2393
2410
|
except ValueError:
|
|
2394
2411
|
print(f"Bad index: {idx_str}")
|
|
2395
2412
|
fig.canvas.draw()
|
|
2396
|
-
elif sub == 't' and cif_tick_series:
|
|
2413
|
+
elif sub == 't' and has_cif and cif_tick_series:
|
|
2397
2414
|
print("Current CIF tick sets:")
|
|
2398
2415
|
for i,(lab,_,_,_,_,color) in enumerate(cif_tick_series):
|
|
2399
2416
|
print(f" {i+1}: {lab} (color {color})")
|
|
@@ -2514,8 +2531,17 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
2514
2531
|
print(f"Error in color menu: {e}")
|
|
2515
2532
|
elif key == 'r':
|
|
2516
2533
|
try:
|
|
2534
|
+
has_cif = False
|
|
2535
|
+
try:
|
|
2536
|
+
has_cif = any(f.lower().endswith('.cif') for f in args.files)
|
|
2537
|
+
except Exception:
|
|
2538
|
+
pass
|
|
2517
2539
|
while True:
|
|
2518
|
-
|
|
2540
|
+
rename_opts = "c=curve"
|
|
2541
|
+
if has_cif:
|
|
2542
|
+
rename_opts += ", t=cif tick label"
|
|
2543
|
+
rename_opts += ", x=x-axis, y=y-axis, q=return"
|
|
2544
|
+
mode = input(f"Rename ({rename_opts}): ").strip().lower()
|
|
2519
2545
|
if mode == 'q':
|
|
2520
2546
|
break
|
|
2521
2547
|
if mode == '':
|
|
@@ -3012,6 +3038,10 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3012
3038
|
print(" tt top X axis title")
|
|
3013
3039
|
print(" lt left Y axis title")
|
|
3014
3040
|
print(" rt right Y axis title")
|
|
3041
|
+
print(" bl bottom plot frame line (spine)")
|
|
3042
|
+
print(" tl top plot frame line (spine)")
|
|
3043
|
+
print(" ll left plot frame line (spine)")
|
|
3044
|
+
print(" rl right plot frame line (spine)")
|
|
3015
3045
|
print(" list show state q return")
|
|
3016
3046
|
cmd = input("Enter code(s): ").strip().lower()
|
|
3017
3047
|
if not cmd:
|
|
@@ -3021,11 +3051,15 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3021
3051
|
parts = cmd.split()
|
|
3022
3052
|
if parts == ['list']:
|
|
3023
3053
|
print_tick_state()
|
|
3054
|
+
# Print spine (frame) visibility
|
|
3055
|
+
print("Spine (frame) visibility:")
|
|
3056
|
+
for spine in ['bottom','top','left','right']:
|
|
3057
|
+
vis = get_spine_visible(spine)
|
|
3058
|
+
print(f" {spine:<6}: {'ON ' if vis else 'off'}")
|
|
3024
3059
|
continue
|
|
3025
3060
|
push_state("tick-toggle")
|
|
3026
|
-
# Helper to sync font sizes after structural changes
|
|
3027
|
-
# (Use shared sync_fonts helper)
|
|
3028
3061
|
for p in parts:
|
|
3062
|
+
# Axis title toggles
|
|
3029
3063
|
if p in ('bt','tt','lt','rt'):
|
|
3030
3064
|
if p == 'bt':
|
|
3031
3065
|
cur = ax.get_xlabel()
|
|
@@ -3038,7 +3072,6 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3038
3072
|
elif p == 'tt':
|
|
3039
3073
|
vis = getattr(ax, '_top_xlabel_on', False)
|
|
3040
3074
|
if not vis:
|
|
3041
|
-
# Create duplicate artist if not existing
|
|
3042
3075
|
lbl_text = ax.get_xlabel()
|
|
3043
3076
|
if not lbl_text:
|
|
3044
3077
|
print("No bottom X label to duplicate.")
|
|
@@ -3066,12 +3099,10 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3066
3099
|
elif p == 'rt':
|
|
3067
3100
|
vis = getattr(ax, '_right_ylabel_on', False)
|
|
3068
3101
|
if not vis:
|
|
3069
|
-
# Create manual duplicate text artist
|
|
3070
3102
|
base = ax.get_ylabel()
|
|
3071
3103
|
if not base:
|
|
3072
3104
|
print("No left Y label to duplicate.")
|
|
3073
3105
|
else:
|
|
3074
|
-
# Remove any previous artist
|
|
3075
3106
|
if hasattr(ax,'_right_ylabel_artist') and ax._right_ylabel_artist is not None:
|
|
3076
3107
|
try: ax._right_ylabel_artist.remove()
|
|
3077
3108
|
except Exception: pass
|
|
@@ -3089,6 +3120,15 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3089
3120
|
ax._right_ylabel_on = False
|
|
3090
3121
|
print("Hid right Y axis title")
|
|
3091
3122
|
continue
|
|
3123
|
+
# Plot frame (spine) toggles
|
|
3124
|
+
if p in ('bl','tl','ll','rl'):
|
|
3125
|
+
spine_map = {'bl':'bottom','tl':'top','ll':'left','rl':'right'}
|
|
3126
|
+
spine = spine_map[p]
|
|
3127
|
+
vis = get_spine_visible(spine)
|
|
3128
|
+
set_spine_visible(spine, not vis)
|
|
3129
|
+
print(f"Toggled {spine} spine -> {'ON' if not vis else 'off'}")
|
|
3130
|
+
continue
|
|
3131
|
+
# Tick toggles
|
|
3092
3132
|
if p in tick_state:
|
|
3093
3133
|
tick_state[p] = not tick_state[p]
|
|
3094
3134
|
print(f"Toggled {p} -> {'ON' if tick_state[p] else 'off'}")
|
|
@@ -3262,17 +3302,24 @@ def interactive_menu(fig, ax, y_data_list, x_data_list, labels, orig_y,
|
|
|
3262
3302
|
#
|
|
3263
3303
|
# ---------------- Argument Parsing ----------------
|
|
3264
3304
|
parser = argparse.ArgumentParser(
|
|
3265
|
-
description="batplot: Plot diffraction / PDF / XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir)\n"
|
|
3305
|
+
description="batplot: Plot diffraction / PDF / XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir, .txt)\n"
|
|
3266
3306
|
" --delta or -d : vertical offset between curves (default 0.0 if --stack)\n"
|
|
3267
3307
|
" --xrange min max : X-axis range (2θ or Q), Example: --xrange 2 10\n"
|
|
3268
3308
|
" --out or -o : output image filename (default SVG), Example: --out figure.svg\n"
|
|
3269
|
-
" --xaxis : X-axis type override if the file extension is
|
|
3309
|
+
" --xaxis : X-axis type override if the file extension is not recognized (choose from: 2theta, Q, r, k, energy, rft, or 'user defined')\n"
|
|
3270
3310
|
" --wl : global wavelength (Å) for Q conversion, Example: --wl 1.5406\n"
|
|
3271
3311
|
" --fullprof : FullProf matrix: xstart xend xstep [wavelength], Example: --fullprof 2 10 0.02 1.5406\n"
|
|
3272
3312
|
" --raw : plot raw intensity values instead of normalized\n"
|
|
3273
3313
|
" --stack : stack curves from top to bottom\n"
|
|
3274
|
-
"test"
|
|
3275
3314
|
" --interactive : keep figure open for interactive editing\n\n"
|
|
3315
|
+
"File type and X-axis selection:\n"
|
|
3316
|
+
" - .qye: X axis is Q\n"
|
|
3317
|
+
" - .gr: X axis is r\n"
|
|
3318
|
+
" - .nor: X axis is Energy (eV)\n"
|
|
3319
|
+
" - .chik: X axis is k\n"
|
|
3320
|
+
" - .chir: X axis is r\n"
|
|
3321
|
+
" - .txt: Treated as generic 2-column data.\n"
|
|
3322
|
+
" If none of the files have a recognized extension, you must specify --xaxis (Q, 2theta, r, k, energy, rft, or 'user defined').\n"
|
|
3276
3323
|
"Example usages:\n"
|
|
3277
3324
|
" batplot file1.xye:1.5406 file2.qye --stack --interactive\n"
|
|
3278
3325
|
" batplot file1.dat file2.dat --xaxis Q --wl 1.5406 --delta 1.0 --out figure.svg\n"
|
|
@@ -3289,7 +3336,7 @@ parser.add_argument("--autoscale", action="store_true")
|
|
|
3289
3336
|
parser.add_argument("--xrange", "-r", nargs=2, type=float)
|
|
3290
3337
|
parser.add_argument("--out", "-o", type=str)
|
|
3291
3338
|
parser.add_argument("--errors", action="store_true")
|
|
3292
|
-
parser.add_argument("--xaxis",
|
|
3339
|
+
parser.add_argument("--xaxis", type=str)
|
|
3293
3340
|
parser.add_argument("--convert", "-c", nargs="+")
|
|
3294
3341
|
parser.add_argument("--wl", type=float)
|
|
3295
3342
|
parser.add_argument("--fullprof", nargs="+", type=float)
|
|
@@ -3326,7 +3373,7 @@ args = parser.parse_args()
|
|
|
3326
3373
|
# ---------------- Batch Processing (directory or 'all') ----------------
|
|
3327
3374
|
def batch_process(directory: str, args):
|
|
3328
3375
|
print(f"Batch mode: scanning {directory}")
|
|
3329
|
-
supported_ext = {'.xye', '.xy', '.qye', '.dat', '.csv', '.gr', '.nor', '.chik', '.chir'} # added .xy
|
|
3376
|
+
supported_ext = {'.xye', '.xy', '.qye', '.dat', '.csv', '.gr', '.nor', '.chik', '.chir', '.txt'} # added .xy, .txt
|
|
3330
3377
|
out_dir = os.path.join(directory, "batplot_svg")
|
|
3331
3378
|
os.makedirs(out_dir, exist_ok=True)
|
|
3332
3379
|
files = [f for f in sorted(os.listdir(directory))
|
|
@@ -3364,20 +3411,27 @@ def batch_process(directory: str, args):
|
|
|
3364
3411
|
x, y = data[:,0], data[:,1]; e = data[:,2] if data.shape[1] >= 3 else None
|
|
3365
3412
|
axis_mode = 'rft'
|
|
3366
3413
|
else:
|
|
3414
|
+
# Support .txt as generic 2-column data
|
|
3367
3415
|
data = np.loadtxt(fpath, comments="#")
|
|
3368
3416
|
if data.ndim == 1: data = data.reshape(1, -1)
|
|
3369
3417
|
if data.shape[1] < 2: raise ValueError("Invalid 2-column data")
|
|
3370
3418
|
x, y = data[:,0], data[:,1]
|
|
3371
3419
|
e = data[:,2] if data.shape[1] >= 3 else None
|
|
3372
|
-
# Decide axis: priority: user --xaxis, .qye -> Q,
|
|
3420
|
+
# Decide axis: priority: user --xaxis, .qye -> Q, .gr -> r, .nor -> energy, .chik -> k, .chir -> rft, else prompt for --xaxis
|
|
3373
3421
|
if ext == '.qye':
|
|
3374
3422
|
axis_mode = 'Q'
|
|
3375
|
-
elif
|
|
3423
|
+
elif ext == '.gr':
|
|
3424
|
+
axis_mode = 'r'
|
|
3425
|
+
elif ext == '.nor':
|
|
3426
|
+
axis_mode = 'energy'
|
|
3427
|
+
elif 'chik' in ext:
|
|
3428
|
+
axis_mode = 'k'
|
|
3429
|
+
elif 'chir' in ext:
|
|
3430
|
+
axis_mode = 'rft'
|
|
3431
|
+
elif args.xaxis:
|
|
3376
3432
|
axis_mode = args.xaxis
|
|
3377
|
-
elif args.wl is not None:
|
|
3378
|
-
axis_mode = 'Q'
|
|
3379
3433
|
else:
|
|
3380
|
-
|
|
3434
|
+
raise ValueError("Cannot determine X-axis type for file {} (need .qye / .gr / .nor / .chik / .chir or specify --xaxis).".format(fname))
|
|
3381
3435
|
# ---- Convert to Q if needed ----
|
|
3382
3436
|
if axis_mode == 'Q' and ext not in ('.qye', '.gr', '.nor'):
|
|
3383
3437
|
if args.wl is None:
|
|
@@ -3686,28 +3740,27 @@ raw_y_full_list = []
|
|
|
3686
3740
|
offsets_list = []
|
|
3687
3741
|
|
|
3688
3742
|
# ---------------- Determine X-axis type ----------------
|
|
3743
|
+
def _ext_token(path):
|
|
3744
|
+
return os.path.splitext(path)[1].lower() # includes leading dot
|
|
3689
3745
|
any_qye = any(f.lower().endswith(".qye") for f in args.files)
|
|
3690
3746
|
any_gr = any(f.lower().endswith(".gr") for f in args.files)
|
|
3691
|
-
any_nor = any(f.lower().endswith(".nor") for f in args.files)
|
|
3747
|
+
any_nor = any(f.lower().endswith(".nor") for f in args.files)
|
|
3748
|
+
any_chik = any("chik" in _ext_token(f) for f in args.files)
|
|
3749
|
+
any_chir = any("chir" in _ext_token(f) for f in args.files)
|
|
3750
|
+
any_txt = any(f.lower().endswith(".txt") for f in args.files)
|
|
3692
3751
|
any_cif = any(f.lower().endswith(".cif") for f in args.files)
|
|
3693
3752
|
non_cif_count = sum(0 if f.lower().endswith('.cif') else 1 for f in args.files)
|
|
3694
3753
|
cif_only = any_cif and non_cif_count == 0
|
|
3695
|
-
def _ext_token(path):
|
|
3696
|
-
return os.path.splitext(path)[1].lower() # includes leading dot
|
|
3697
|
-
any_chik = any("chik" in _ext_token(f) for f in args.files)
|
|
3698
|
-
any_chir = any("chir" in _ext_token(f) for f in args.files)
|
|
3699
3754
|
any_lambda = any(":" in f for f in args.files) or args.wl is not None
|
|
3700
3755
|
|
|
3701
3756
|
# Incompatibilities (no mixing of fundamentally different axis domains)
|
|
3702
|
-
if sum(bool(x) for x in (any_gr, any_nor, (any_qye or any_lambda or any_cif))) > 1 or \
|
|
3703
|
-
(any_gr and args.xaxis in ("Q", "2theta", "r")) or \
|
|
3704
|
-
(any_nor and args.xaxis in ("Q", "2theta", "r")):
|
|
3705
|
-
raise ValueError("Cannot mix .gr (r), .nor (energy), and Q/2θ/CIF data. Plot them in separate runs.")
|
|
3706
|
-
|
|
3707
3757
|
if sum(bool(x) for x in (any_gr, any_nor, any_chik, any_chir, (any_qye or any_lambda or any_cif))) > 1:
|
|
3708
|
-
raise ValueError("Cannot mix .gr (r), .nor (energy), .chik (k), .chir (FT-EXAFS R) and Q/2θ/CIF data together. Split runs.")
|
|
3758
|
+
raise ValueError("Cannot mix .gr (r), .nor (energy), .chik (k), .chir (FT-EXAFS R), and Q/2θ/CIF data together. Split runs.")
|
|
3709
3759
|
|
|
3710
|
-
|
|
3760
|
+
# Automatic axis selection based on file extensions
|
|
3761
|
+
if any_qye:
|
|
3762
|
+
axis_mode = "Q"
|
|
3763
|
+
elif any_gr:
|
|
3711
3764
|
axis_mode = "r"
|
|
3712
3765
|
elif any_nor:
|
|
3713
3766
|
axis_mode = "energy"
|
|
@@ -3715,6 +3768,12 @@ elif any_chik:
|
|
|
3715
3768
|
axis_mode = "k"
|
|
3716
3769
|
elif any_chir:
|
|
3717
3770
|
axis_mode = "rft"
|
|
3771
|
+
elif any_txt:
|
|
3772
|
+
# .txt is generic, require --xaxis
|
|
3773
|
+
if args.xaxis:
|
|
3774
|
+
axis_mode = args.xaxis
|
|
3775
|
+
else:
|
|
3776
|
+
raise ValueError("Cannot determine X-axis type for .txt files. Please specify --xaxis (Q, 2theta, r, k, energy, rft, or 'user defined').")
|
|
3718
3777
|
elif any_qye or any_lambda or any_cif:
|
|
3719
3778
|
if args.xaxis and args.xaxis.lower() in ("2theta","two_theta","tth"):
|
|
3720
3779
|
axis_mode = "2theta"
|
|
@@ -3723,7 +3782,7 @@ elif any_qye or any_lambda or any_cif:
|
|
|
3723
3782
|
elif args.xaxis:
|
|
3724
3783
|
axis_mode = args.xaxis
|
|
3725
3784
|
else:
|
|
3726
|
-
raise ValueError("Cannot determine X-axis type (need .qye / .gr / .nor / .chik / .chir / .cif / wavelength / --xaxis).")
|
|
3785
|
+
raise ValueError("Cannot determine X-axis type (need .qye / .gr / .nor / .chik / .chir / .cif / wavelength / --xaxis). For .txt or unknown file types, use --xaxis Q, 2theta, r, k, energy, rft, or 'user defined'.")
|
|
3727
3786
|
|
|
3728
3787
|
use_Q = axis_mode == "Q"
|
|
3729
3788
|
use_2th = axis_mode == "2theta"
|
|
@@ -4254,7 +4313,10 @@ elif use_k: x_label = r"k ($\mathrm{\AA}^{-1}$)"
|
|
|
4254
4313
|
elif use_rft: x_label = "Radial distance (Å)"
|
|
4255
4314
|
elif use_Q: x_label = r"Q ($\mathrm{\AA}^{-1}$)"
|
|
4256
4315
|
elif use_2th: x_label = r"$2\theta$ (deg)"
|
|
4257
|
-
|
|
4316
|
+
elif args.xaxis:
|
|
4317
|
+
x_label = str(args.xaxis)
|
|
4318
|
+
else:
|
|
4319
|
+
x_label = "X"
|
|
4258
4320
|
ax.set_xlabel(x_label, fontsize=16)
|
|
4259
4321
|
if args.raw:
|
|
4260
4322
|
ax.set_ylabel("Intensity", fontsize=16)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.7
|
|
4
4
|
Summary: Interactive plotting for XRD, PDF, and XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir)
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Tian Dai <tianda@uio.no>
|
|
6
6
|
Requires-Python: >=3.7
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: numpy
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "batplot"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.7"
|
|
8
8
|
description = "Interactive plotting for XRD, PDF, and XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir)"
|
|
9
9
|
authors = [
|
|
10
|
-
{ name = "
|
|
10
|
+
{ name = "Tian Dai", email = "tianda@uio.no" }
|
|
11
11
|
]
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.7"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|