batplot 1.8.4__py3-none-any.whl → 1.8.6__py3-none-any.whl
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/__init__.py +1 -1
- batplot/args.py +22 -4
- batplot/batch.py +12 -0
- batplot/batplot.py +340 -126
- batplot/converters.py +170 -122
- batplot/cpc_interactive.py +319 -161
- batplot/data/USER_MANUAL.md +49 -0
- batplot/electrochem_interactive.py +120 -80
- batplot/interactive.py +1763 -75
- batplot/modes.py +12 -11
- batplot/operando.py +22 -0
- batplot/operando_ec_interactive.py +390 -16
- batplot/session.py +85 -9
- batplot/style.py +198 -21
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/METADATA +1 -1
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/RECORD +20 -20
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/WHEEL +1 -1
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/licenses/LICENSE +0 -0
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/entry_points.txt +0 -0
- {batplot-1.8.4.dist-info → batplot-1.8.6.dist-info}/top_level.txt +0 -0
batplot/batplot.py
CHANGED
|
@@ -9,7 +9,7 @@ from .electrochem_interactive import electrochem_interactive_menu
|
|
|
9
9
|
from .args import parse_args as _bp_parse_args
|
|
10
10
|
from .interactive import interactive_menu
|
|
11
11
|
from .batch import batch_process, batch_process_ec
|
|
12
|
-
from .converters import
|
|
12
|
+
from .converters import convert_xrd_data
|
|
13
13
|
from .session import (
|
|
14
14
|
dump_session as _bp_dump_session,
|
|
15
15
|
load_ec_session,
|
|
@@ -422,7 +422,10 @@ def batplot_main() -> int:
|
|
|
422
422
|
# Ensure font and canvas settings match GC/dQdV
|
|
423
423
|
_plt.rcParams.update({
|
|
424
424
|
'font.family': 'sans-serif',
|
|
425
|
-
|
|
425
|
+
# Prefer DejaVu Sans first because it has good Unicode
|
|
426
|
+
# coverage (including subscript/superscript digits), then
|
|
427
|
+
# fall back to other common sans-serif fonts.
|
|
428
|
+
'font.sans-serif': ['DejaVu Sans', 'Arial', 'Helvetica', 'STIXGeneral', 'Liberation Sans', 'Arial Unicode MS'],
|
|
426
429
|
'mathtext.fontset': 'dejavusans',
|
|
427
430
|
'font.size': 16
|
|
428
431
|
})
|
|
@@ -616,8 +619,10 @@ def batplot_main() -> int:
|
|
|
616
619
|
# Set global default font
|
|
617
620
|
plt.rcParams.update({
|
|
618
621
|
'font.family': 'sans-serif',
|
|
619
|
-
|
|
620
|
-
|
|
622
|
+
# Use DejaVu Sans first to ensure good Unicode coverage (subscripts,
|
|
623
|
+
# superscripts, Greek, etc.), then fall back to other common fonts.
|
|
624
|
+
'font.sans-serif': ['DejaVu Sans', 'Arial', 'Helvetica', 'STIXGeneral', 'Liberation Sans', 'Arial Unicode MS'],
|
|
625
|
+
'mathtext.fontset': 'dejavusans', # keeps math consistent with sans-serif
|
|
621
626
|
'font.size': 16
|
|
622
627
|
})
|
|
623
628
|
|
|
@@ -1165,11 +1170,9 @@ def batplot_main() -> int:
|
|
|
1165
1170
|
print(f"Skipped {file_basename}: unsupported format (must be .csv, .xlsx, or .mpt)")
|
|
1166
1171
|
continue
|
|
1167
1172
|
|
|
1168
|
-
# Assign colors: distinct hue
|
|
1173
|
+
# Assign colors: distinct hue per file
|
|
1169
1174
|
capacity_color = capacity_colors[file_idx % len(capacity_colors)]
|
|
1170
1175
|
efficiency_color = efficiency_colors[file_idx % len(efficiency_colors)]
|
|
1171
|
-
# Generate discharge color using the same function as interactive menu
|
|
1172
|
-
discharge_color = _generate_similar_color(capacity_color)
|
|
1173
1176
|
|
|
1174
1177
|
file_data.append({
|
|
1175
1178
|
'filename': file_basename,
|
|
@@ -1179,7 +1182,6 @@ def batplot_main() -> int:
|
|
|
1179
1182
|
'cap_discharge': cap_discharge,
|
|
1180
1183
|
'eff': eff,
|
|
1181
1184
|
'color': capacity_color,
|
|
1182
|
-
'discharge_color': discharge_color,
|
|
1183
1185
|
'eff_color': efficiency_color,
|
|
1184
1186
|
'visible': True
|
|
1185
1187
|
})
|
|
@@ -1207,7 +1209,7 @@ def batplot_main() -> int:
|
|
|
1207
1209
|
cap_charge = file_info['cap_charge']
|
|
1208
1210
|
cap_discharge = file_info['cap_discharge']
|
|
1209
1211
|
eff = file_info['eff']
|
|
1210
|
-
color = file_info['color'] #
|
|
1212
|
+
color = file_info['color'] # Base color for capacity (both charge/discharge)
|
|
1211
1213
|
eff_color = file_info['eff_color'] # Cold color for efficiency
|
|
1212
1214
|
label = file_info['filename']
|
|
1213
1215
|
|
|
@@ -1221,16 +1223,30 @@ def batplot_main() -> int:
|
|
|
1221
1223
|
label_dch = f'{label} (Dch)'
|
|
1222
1224
|
label_eff = f'{label} (Eff)'
|
|
1223
1225
|
|
|
1224
|
-
#
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1226
|
+
# Capacity curves: same color, different fill style
|
|
1227
|
+
# - Charge: filled square
|
|
1228
|
+
# - Discharge: hollow square (edge only)
|
|
1229
|
+
sc_charge = ax.scatter(
|
|
1230
|
+
cyc_nums,
|
|
1231
|
+
cap_charge,
|
|
1232
|
+
label=label_chg,
|
|
1233
|
+
s=32,
|
|
1234
|
+
zorder=3,
|
|
1235
|
+
alpha=0.8,
|
|
1236
|
+
marker='s',
|
|
1237
|
+
color=color,
|
|
1238
|
+
)
|
|
1239
|
+
sc_discharge = ax.scatter(
|
|
1240
|
+
cyc_nums,
|
|
1241
|
+
cap_discharge,
|
|
1242
|
+
label=label_dch,
|
|
1243
|
+
s=32,
|
|
1244
|
+
zorder=3,
|
|
1245
|
+
alpha=0.8,
|
|
1246
|
+
marker='s',
|
|
1247
|
+
facecolor='none',
|
|
1248
|
+
edgecolor=color,
|
|
1249
|
+
)
|
|
1234
1250
|
sc_eff = ax2.scatter(cyc_nums, eff, color=eff_color, marker='^', label=label_eff,
|
|
1235
1251
|
s=40, alpha=0.7, zorder=3)
|
|
1236
1252
|
|
|
@@ -1247,27 +1263,25 @@ def batplot_main() -> int:
|
|
|
1247
1263
|
h1, l1 = ax.get_legend_handles_labels()
|
|
1248
1264
|
h2, l2 = ax2.get_legend_handles_labels()
|
|
1249
1265
|
combined_handles = h1 + h2
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
except Exception:
|
|
1270
|
-
pass
|
|
1266
|
+
combined_labels = l1 + l2
|
|
1267
|
+
if combined_handles:
|
|
1268
|
+
# Don't use labelcolor='linecolor' for scatter plots with hollow markers
|
|
1269
|
+
# as it causes issues with color extraction
|
|
1270
|
+
leg = ax.legend(
|
|
1271
|
+
combined_handles, combined_labels,
|
|
1272
|
+
loc='best',
|
|
1273
|
+
frameon=False,
|
|
1274
|
+
handlelength=1.0,
|
|
1275
|
+
handletextpad=0.35,
|
|
1276
|
+
labelspacing=0.25,
|
|
1277
|
+
borderaxespad=0.5,
|
|
1278
|
+
borderpad=0.3,
|
|
1279
|
+
columnspacing=0.6,
|
|
1280
|
+
)
|
|
1281
|
+
except Exception as e:
|
|
1282
|
+
print(f"Warning: Could not create CPC legend: {e}")
|
|
1283
|
+
import traceback
|
|
1284
|
+
traceback.print_exc()
|
|
1271
1285
|
|
|
1272
1286
|
# Adjust layout to ensure top and bottom labels/titles are visible
|
|
1273
1287
|
fig.subplots_adjust(left=0.12, right=0.88, top=0.88, bottom=0.15)
|
|
@@ -1842,6 +1856,11 @@ def batplot_main() -> int:
|
|
|
1842
1856
|
_plt.show(block=False)
|
|
1843
1857
|
except Exception:
|
|
1844
1858
|
pass
|
|
1859
|
+
# Seed last-session path so 'os' overwrite command is available immediately
|
|
1860
|
+
try:
|
|
1861
|
+
fig._last_session_save_path = os.path.abspath(sess_path)
|
|
1862
|
+
except Exception:
|
|
1863
|
+
pass
|
|
1845
1864
|
try:
|
|
1846
1865
|
source_list = list(getattr(fig, '_bp_source_paths', []) or [])
|
|
1847
1866
|
sess_abs = os.path.abspath(sess_path)
|
|
@@ -1877,6 +1896,11 @@ def batplot_main() -> int:
|
|
|
1877
1896
|
_plt.show(block=False)
|
|
1878
1897
|
except Exception:
|
|
1879
1898
|
pass
|
|
1899
|
+
# Seed last-session path so 'os' overwrite command is available immediately
|
|
1900
|
+
try:
|
|
1901
|
+
fig2._last_session_save_path = os.path.abspath(sess_path)
|
|
1902
|
+
except Exception:
|
|
1903
|
+
pass
|
|
1880
1904
|
try:
|
|
1881
1905
|
if operando_ec_interactive_menu is not None:
|
|
1882
1906
|
operando_ec_interactive_menu(fig2, ax2, im2, cbar2, ec_ax2)
|
|
@@ -1905,6 +1929,11 @@ def batplot_main() -> int:
|
|
|
1905
1929
|
_plt.show(block=False)
|
|
1906
1930
|
except Exception:
|
|
1907
1931
|
pass
|
|
1932
|
+
# Seed last-session path so 'os' overwrite command is available immediately
|
|
1933
|
+
try:
|
|
1934
|
+
fig_c._last_session_save_path = os.path.abspath(sess_path)
|
|
1935
|
+
except Exception:
|
|
1936
|
+
pass
|
|
1908
1937
|
try:
|
|
1909
1938
|
if cpc_interactive_menu is not None:
|
|
1910
1939
|
cpc_interactive_menu(fig_c, ax_c, ax2_c, sc_c, sc_d, sc_e, file_data=file_data)
|
|
@@ -1964,16 +1993,58 @@ def batplot_main() -> int:
|
|
|
1964
1993
|
y_loaded = sess.get('y_data', []) # stored plotted (baseline+offset) values
|
|
1965
1994
|
orig_loaded = sess.get('orig_y', []) # stored baseline (normalized/raw w/out offsets)
|
|
1966
1995
|
offsets_saved = sess.get('offsets', [])
|
|
1996
|
+
# Restore processed data (for smooth/reduce operations)
|
|
1997
|
+
original_x_data_list = sess.get('original_x_data_list')
|
|
1998
|
+
original_y_data_list = sess.get('original_y_data_list')
|
|
1999
|
+
smooth_settings = sess.get('smooth_settings')
|
|
2000
|
+
if original_x_data_list is not None:
|
|
2001
|
+
fig._original_x_data_list = [np.array(a) for a in original_x_data_list]
|
|
2002
|
+
if original_y_data_list is not None:
|
|
2003
|
+
fig._original_y_data_list = [np.array(a) for a in original_y_data_list]
|
|
2004
|
+
full_processed_x_data_list = sess.get('full_processed_x_data_list')
|
|
2005
|
+
full_processed_y_data_list = sess.get('full_processed_y_data_list')
|
|
2006
|
+
if full_processed_x_data_list is not None:
|
|
2007
|
+
fig._full_processed_x_data_list = [np.array(a) for a in full_processed_x_data_list]
|
|
2008
|
+
if full_processed_y_data_list is not None:
|
|
2009
|
+
fig._full_processed_y_data_list = [np.array(a) for a in full_processed_y_data_list]
|
|
2010
|
+
if smooth_settings is not None:
|
|
2011
|
+
fig._smooth_settings = dict(smooth_settings)
|
|
2012
|
+
last_smooth_settings = sess.get('last_smooth_settings')
|
|
2013
|
+
if last_smooth_settings is not None:
|
|
2014
|
+
fig._last_smooth_settings = dict(last_smooth_settings)
|
|
2015
|
+
# Restore derivative data (for derivative operations)
|
|
2016
|
+
pre_derivative_x_data_list = sess.get('pre_derivative_x_data_list')
|
|
2017
|
+
pre_derivative_y_data_list = sess.get('pre_derivative_y_data_list')
|
|
2018
|
+
pre_derivative_ylabel = sess.get('pre_derivative_ylabel')
|
|
2019
|
+
derivative_order = sess.get('derivative_order')
|
|
2020
|
+
if pre_derivative_x_data_list is not None:
|
|
2021
|
+
fig._pre_derivative_x_data_list = [np.array(a) for a in pre_derivative_x_data_list]
|
|
2022
|
+
if pre_derivative_y_data_list is not None:
|
|
2023
|
+
fig._pre_derivative_y_data_list = [np.array(a) for a in pre_derivative_y_data_list]
|
|
2024
|
+
if pre_derivative_ylabel is not None:
|
|
2025
|
+
fig._pre_derivative_ylabel = str(pre_derivative_ylabel)
|
|
2026
|
+
if derivative_order is not None:
|
|
2027
|
+
fig._derivative_order = int(derivative_order)
|
|
2028
|
+
derivative_reversed = sess.get('derivative_reversed')
|
|
2029
|
+
if derivative_reversed is not None:
|
|
2030
|
+
fig._derivative_reversed = bool(derivative_reversed)
|
|
1967
2031
|
n_curves = len(x_loaded)
|
|
1968
2032
|
for i in range(n_curves):
|
|
1969
|
-
|
|
2033
|
+
# Ensure arrays are 1D and have matching shapes
|
|
2034
|
+
x_arr = np.asarray(x_loaded[i], dtype=float).flatten()
|
|
1970
2035
|
off = offsets_saved[i] if i < len(offsets_saved) else 0.0
|
|
1971
2036
|
if orig_loaded and i < len(orig_loaded):
|
|
1972
|
-
base = np.
|
|
2037
|
+
base = np.asarray(orig_loaded[i], dtype=float).flatten()
|
|
1973
2038
|
else:
|
|
1974
2039
|
# Fallback: derive baseline by subtracting offset from stored y (handles legacy sessions)
|
|
1975
|
-
y_arr_full = np.
|
|
2040
|
+
y_arr_full = np.asarray(y_loaded[i], dtype=float).flatten() if i < len(y_loaded) else np.array([], dtype=float)
|
|
1976
2041
|
base = y_arr_full - off
|
|
2042
|
+
# Ensure x and y have matching lengths
|
|
2043
|
+
if x_arr.size != base.size:
|
|
2044
|
+
print(f"Warning: Curve {i+1} has mismatched x/y lengths ({x_arr.size} vs {base.size}). Trimming to match.")
|
|
2045
|
+
min_len = min(x_arr.size, base.size)
|
|
2046
|
+
x_arr = x_arr[:min_len]
|
|
2047
|
+
base = base[:min_len]
|
|
1977
2048
|
y_plot = base + off
|
|
1978
2049
|
x_data_list.append(x_arr)
|
|
1979
2050
|
orig_y.append(base)
|
|
@@ -2165,6 +2236,14 @@ def batplot_main() -> int:
|
|
|
2165
2236
|
fig._tick_lengths['minor'] = minor_len
|
|
2166
2237
|
except Exception:
|
|
2167
2238
|
pass
|
|
2239
|
+
# Tick direction restore (t submenu)
|
|
2240
|
+
try:
|
|
2241
|
+
tick_direction = sess.get('tick_direction', 'out')
|
|
2242
|
+
if tick_direction:
|
|
2243
|
+
setattr(fig, '_tick_direction', tick_direction)
|
|
2244
|
+
ax.tick_params(axis='both', which='both', direction=tick_direction)
|
|
2245
|
+
except Exception:
|
|
2246
|
+
pass
|
|
2168
2247
|
|
|
2169
2248
|
# Restore WASD state (spine, ticks, labels, title visibility for all 4 sides)
|
|
2170
2249
|
try:
|
|
@@ -2277,8 +2356,24 @@ def batplot_main() -> int:
|
|
|
2277
2356
|
cif_hkl_label_map = {k: dict(v) for k,v in sess.get('cif_hkl_label_map', {}).items()}
|
|
2278
2357
|
cif_numbering_enabled = True
|
|
2279
2358
|
cif_extend_suspended = False
|
|
2280
|
-
|
|
2281
|
-
|
|
2359
|
+
# Restore CIF visibility flags - default to False for hkl (labels hidden by default)
|
|
2360
|
+
# and True for titles (shown by default)
|
|
2361
|
+
show_cif_hkl = bool(sess.get('show_cif_hkl', False))
|
|
2362
|
+
show_cif_titles = bool(sess.get('show_cif_titles', True))
|
|
2363
|
+
|
|
2364
|
+
# Store CIF state in __main__ module for interactive menu to access
|
|
2365
|
+
# This ensures CIF commands (z, hkl, j) are available in the menu
|
|
2366
|
+
try:
|
|
2367
|
+
_bp_module = sys.modules.get('__main__')
|
|
2368
|
+
if _bp_module is not None and cif_tick_series:
|
|
2369
|
+
setattr(_bp_module, 'cif_tick_series', list(cif_tick_series))
|
|
2370
|
+
setattr(_bp_module, 'cif_hkl_map', cif_hkl_map)
|
|
2371
|
+
setattr(_bp_module, 'cif_hkl_label_map', cif_hkl_label_map)
|
|
2372
|
+
setattr(_bp_module, 'show_cif_hkl', bool(show_cif_hkl))
|
|
2373
|
+
setattr(_bp_module, 'show_cif_titles', bool(show_cif_titles))
|
|
2374
|
+
setattr(_bp_module, 'cif_extend_suspended', False)
|
|
2375
|
+
except Exception:
|
|
2376
|
+
pass
|
|
2282
2377
|
# Provide minimal stubs to satisfy interactive menu dependencies
|
|
2283
2378
|
# Axis mode restoration informs downstream toggles (e.g., CIF conversions, crosshair availability)
|
|
2284
2379
|
axis_mode_restored = sess.get('axis_mode', 'unknown')
|
|
@@ -2337,13 +2432,20 @@ def batplot_main() -> int:
|
|
|
2337
2432
|
if not cif_tick_series:
|
|
2338
2433
|
return
|
|
2339
2434
|
try:
|
|
2340
|
-
# Preserve
|
|
2435
|
+
# Preserve current limits before drawing - use actual current limits
|
|
2436
|
+
# to prevent any movement when toggling
|
|
2341
2437
|
prev_xlim = ax.get_xlim()
|
|
2342
2438
|
prev_ylim = ax.get_ylim()
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2439
|
+
|
|
2440
|
+
# Use current ylim as fixed reference to prevent incremental movement
|
|
2441
|
+
# This ensures that repeated 'z' commands don't cause drift
|
|
2442
|
+
# Store it only once on first call, then reuse
|
|
2443
|
+
if not hasattr(ax, '_cif_initial_ylim'):
|
|
2444
|
+
ax._cif_initial_ylim = tuple(prev_ylim)
|
|
2445
|
+
fixed_ylim = ax._cif_initial_ylim
|
|
2446
|
+
fixed_yr = fixed_ylim[1] - fixed_ylim[0]
|
|
2447
|
+
if fixed_yr <= 0: fixed_yr = 1.0
|
|
2448
|
+
|
|
2347
2449
|
# Check visibility flag first
|
|
2348
2450
|
show_titles_local = bool(show_cif_titles) # Use closure variable from outer scope
|
|
2349
2451
|
# Also check figure attribute and module attribute as fallback
|
|
@@ -2357,38 +2459,53 @@ def batplot_main() -> int:
|
|
|
2357
2459
|
show_titles_local = bool(getattr(_bp_module, 'show_cif_titles', show_titles_local))
|
|
2358
2460
|
except Exception:
|
|
2359
2461
|
pass
|
|
2360
|
-
|
|
2462
|
+
|
|
2463
|
+
# Check hkl visibility - check __main__ module first (where interactive menu stores it)
|
|
2464
|
+
# then fall back to closure variable
|
|
2465
|
+
show_hkl_local = False
|
|
2466
|
+
try:
|
|
2467
|
+
_bp_module = sys.modules.get('__main__')
|
|
2468
|
+
if _bp_module is not None and hasattr(_bp_module, 'show_cif_hkl'):
|
|
2469
|
+
show_hkl_local = bool(getattr(_bp_module, 'show_cif_hkl', False))
|
|
2470
|
+
except Exception:
|
|
2471
|
+
pass
|
|
2472
|
+
# Fall back to closure variable if not found in module
|
|
2473
|
+
if not show_hkl_local:
|
|
2474
|
+
try:
|
|
2475
|
+
show_hkl_local = bool(show_cif_hkl)
|
|
2476
|
+
except Exception:
|
|
2477
|
+
pass
|
|
2478
|
+
|
|
2479
|
+
# Calculate base and spacing based on FIXED y-axis limits (not current)
|
|
2480
|
+
# This prevents incremental movement when toggling
|
|
2361
2481
|
if saved_stack or len(y_data_list) > 1:
|
|
2362
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else
|
|
2363
|
-
base = global_min - 0.08*
|
|
2482
|
+
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else fixed_ylim[0]
|
|
2483
|
+
base = global_min - 0.08*fixed_yr; spacing = 0.05*fixed_yr
|
|
2364
2484
|
else:
|
|
2365
2485
|
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else 0.0
|
|
2366
|
-
base = global_min - 0.06*
|
|
2486
|
+
base = global_min - 0.06*fixed_yr; spacing = 0.04*fixed_yr
|
|
2487
|
+
|
|
2367
2488
|
# Only adjust y-axis limits if titles are visible
|
|
2368
|
-
needed_min = base - (len(cif_tick_series)-1)*spacing - 0.04*
|
|
2489
|
+
needed_min = base - (len(cif_tick_series)-1)*spacing - 0.04*fixed_yr
|
|
2490
|
+
if show_titles_local and needed_min < fixed_ylim[0]:
|
|
2491
|
+
# Expand y-axis only if needed, using fixed limits as reference
|
|
2492
|
+
ax.set_ylim(needed_min, fixed_ylim[1])
|
|
2493
|
+
else:
|
|
2494
|
+
# Restore to fixed limits if no expansion needed
|
|
2495
|
+
ax.set_ylim(fixed_ylim)
|
|
2496
|
+
|
|
2497
|
+
# Get current limits for drawing (after potential expansion)
|
|
2369
2498
|
cur_ylim = ax.get_ylim()
|
|
2370
2499
|
yr = cur_ylim[1] - cur_ylim[0]
|
|
2371
2500
|
if yr <= 0: yr = 1.0
|
|
2372
|
-
|
|
2373
|
-
# Expand y-axis only if needed, using original limits as reference
|
|
2374
|
-
ax.set_ylim(needed_min, orig_ylim[1])
|
|
2375
|
-
cur_ylim = ax.get_ylim()
|
|
2376
|
-
yr = cur_ylim[1] - cur_ylim[0]
|
|
2377
|
-
if yr <= 0: yr = 1.0
|
|
2378
|
-
# Recalculate base with new limits if we expanded
|
|
2379
|
-
if saved_stack or len(y_data_list) > 1:
|
|
2380
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else cur_ylim[0]
|
|
2381
|
-
base = global_min - 0.08*yr; spacing = 0.05*yr
|
|
2382
|
-
else:
|
|
2383
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else 0.0
|
|
2384
|
-
base = global_min - 0.06*yr; spacing = 0.04*yr
|
|
2501
|
+
|
|
2385
2502
|
# Clear previous artifacts
|
|
2386
2503
|
for art in getattr(ax, '_cif_tick_art', []):
|
|
2387
2504
|
try: art.remove()
|
|
2388
2505
|
except Exception: pass
|
|
2389
2506
|
new_art = []
|
|
2390
|
-
show_hkl_local = bool(show_cif_hkl)
|
|
2391
2507
|
wl_any = _session_ensure_wavelength()
|
|
2508
|
+
|
|
2392
2509
|
# Draw each series
|
|
2393
2510
|
for i,(lab,fname,peaksQ,wl,qmax_sim,color) in enumerate(cif_tick_series):
|
|
2394
2511
|
y_line = base - i*spacing
|
|
@@ -2402,12 +2519,18 @@ def batplot_main() -> int:
|
|
|
2402
2519
|
xlow,xhigh = ax.get_xlim()
|
|
2403
2520
|
domain_peaks = [p for p in domain_peaks if xlow <= p <= xhigh]
|
|
2404
2521
|
# Build hkl label map (keys are Q values, not 2θ)
|
|
2405
|
-
|
|
2522
|
+
# Only use label_map if hkl labels are enabled
|
|
2523
|
+
label_map = {}
|
|
2524
|
+
if show_hkl_local:
|
|
2525
|
+
label_map = cif_hkl_label_map.get(fname, {})
|
|
2406
2526
|
if show_hkl_local and len(domain_peaks) > 4000:
|
|
2407
2527
|
show_hkl_local = False # safety
|
|
2528
|
+
label_map = {} # Clear label map if too many peaks
|
|
2408
2529
|
for p in domain_peaks:
|
|
2530
|
+
# Use color from tuple (preserved from session)
|
|
2409
2531
|
ln, = ax.plot([p,p],[y_line, y_line+0.02*yr], color=color, lw=1.0, alpha=0.9, zorder=3)
|
|
2410
2532
|
new_art.append(ln)
|
|
2533
|
+
# Only show hkl labels if explicitly enabled
|
|
2411
2534
|
if show_hkl_local:
|
|
2412
2535
|
# When axis is 2θ convert back to Q to look up hkl label
|
|
2413
2536
|
if use_2th and (wl or wl_any):
|
|
@@ -2415,28 +2538,35 @@ def batplot_main() -> int:
|
|
|
2415
2538
|
Qp = 4*np.pi*np.sin(theta)/(wl if wl is not None else wl_any)
|
|
2416
2539
|
else:
|
|
2417
2540
|
Qp = p
|
|
2418
|
-
|
|
2541
|
+
Qp_rounded = round(Qp, 6)
|
|
2542
|
+
lbl = label_map.get(Qp_rounded)
|
|
2419
2543
|
if lbl:
|
|
2544
|
+
# Use same color as tick line
|
|
2420
2545
|
t_hkl = ax.text(p, y_line+0.022*yr, lbl, ha='center', va='bottom', fontsize=7, rotation=90, color=color)
|
|
2421
2546
|
new_art.append(t_hkl)
|
|
2422
|
-
# Removed numbering prefix; keep one leading space for padding from axis
|
|
2423
2547
|
# Only add title label if show_cif_titles is True
|
|
2424
2548
|
if show_titles_local:
|
|
2425
2549
|
label_text = f" {lab}"
|
|
2550
|
+
# Use color from tuple (preserved from session)
|
|
2426
2551
|
txt = ax.text(prev_xlim[0], y_line+0.005*yr, label_text,
|
|
2427
2552
|
ha='left', va='bottom', fontsize=max(8,int(0.55*plt.rcParams.get('font.size',16))), color=color)
|
|
2428
2553
|
new_art.append(txt)
|
|
2429
2554
|
ax._cif_tick_art = new_art
|
|
2430
|
-
# Restore
|
|
2555
|
+
# Restore x-axis limits
|
|
2431
2556
|
ax.set_xlim(prev_xlim)
|
|
2432
2557
|
# Restore y-axis: if titles are hidden, always restore; if titles are shown, only restore if we didn't need to expand
|
|
2558
|
+
# Use prev_ylim (current limits before drawing) to prevent any movement
|
|
2433
2559
|
if not show_titles_local:
|
|
2434
2560
|
# Titles hidden: always restore original limits
|
|
2435
2561
|
ax.set_ylim(prev_ylim)
|
|
2436
2562
|
elif needed_min >= prev_ylim[0]:
|
|
2437
2563
|
# Titles shown but no expansion needed: restore original limits
|
|
2438
2564
|
ax.set_ylim(prev_ylim)
|
|
2439
|
-
|
|
2565
|
+
else:
|
|
2566
|
+
# Expansion needed: use the minimum of needed_min and prev_ylim[0] to prevent incremental growth
|
|
2567
|
+
# This ensures that repeated toggles don't cause drift
|
|
2568
|
+
new_ymin = min(needed_min, prev_ylim[0])
|
|
2569
|
+
ax.set_ylim(new_ymin, prev_ylim[1])
|
|
2440
2570
|
fig.canvas.draw_idle()
|
|
2441
2571
|
except Exception:
|
|
2442
2572
|
pass
|
|
@@ -2548,28 +2678,58 @@ def batplot_main() -> int:
|
|
|
2548
2678
|
except Exception:
|
|
2549
2679
|
pass
|
|
2550
2680
|
|
|
2681
|
+
# Prepare CIF globals for interactive menu (ensures CIF commands are available)
|
|
2682
|
+
cif_globals_dict = None
|
|
2683
|
+
if cif_tick_series:
|
|
2684
|
+
cif_globals_dict = {
|
|
2685
|
+
'cif_tick_series': list(cif_tick_series),
|
|
2686
|
+
'cif_hkl_map': cif_hkl_map,
|
|
2687
|
+
'cif_hkl_label_map': cif_hkl_label_map,
|
|
2688
|
+
'show_cif_hkl': bool(show_cif_hkl),
|
|
2689
|
+
'show_cif_titles': bool(show_cif_titles),
|
|
2690
|
+
'cif_extend_suspended': False,
|
|
2691
|
+
'keep_canvas_fixed': True,
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2551
2694
|
interactive_menu(fig, ax, y_data_list, x_data_list, labels_list,
|
|
2552
2695
|
orig_y, label_text_objects, delta, x_label, args,
|
|
2553
2696
|
x_full_list, raw_y_full_list, offsets_list,
|
|
2554
|
-
use_Q, use_r, use_E, use_k, use_rft
|
|
2697
|
+
use_Q, use_r, use_E, use_k, use_rft,
|
|
2698
|
+
cif_globals=cif_globals_dict)
|
|
2555
2699
|
plt.show()
|
|
2556
2700
|
exit()
|
|
2557
2701
|
|
|
2558
2702
|
# ---------------- Handle conversion ----------------
|
|
2559
2703
|
if args.convert:
|
|
2560
|
-
if args.
|
|
2561
|
-
print("Error: --
|
|
2562
|
-
|
|
2704
|
+
if not args.files:
|
|
2705
|
+
print("Error: --convert requires file(s) to convert")
|
|
2563
2706
|
exit(1)
|
|
2564
|
-
|
|
2707
|
+
from_param, to_param = args.convert
|
|
2708
|
+
convert_xrd_data(args.files, from_param, to_param)
|
|
2565
2709
|
exit()
|
|
2566
2710
|
|
|
2567
2711
|
# ---------------- Plotting ----------------
|
|
2568
2712
|
offset = 0.0
|
|
2569
2713
|
direction = -1 if args.stack else 1 # stack downward
|
|
2570
2714
|
if args.interactive:
|
|
2715
|
+
# Interactive: keep a reasonably compact default size so the window
|
|
2716
|
+
# fits well on most screens; margins are handled by the menu logic.
|
|
2571
2717
|
plt.ion()
|
|
2572
|
-
|
|
2718
|
+
figsize = (8, 6)
|
|
2719
|
+
else:
|
|
2720
|
+
# Non-interactive (no --i): use a slightly larger canvas so that labels,
|
|
2721
|
+
# titles, legends, and CIF ticks are not clipped even with long filenames.
|
|
2722
|
+
# The size (9, 6.4) keeps a similar aspect ratio but with a bit more room
|
|
2723
|
+
# than the interactive default while still fitting comfortably on screen.
|
|
2724
|
+
figsize = (9.5, 6.4)
|
|
2725
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
2726
|
+
|
|
2727
|
+
# Set consistent margins for all modes.
|
|
2728
|
+
# This prevents labels/titles from being cut off at the edges.
|
|
2729
|
+
try:
|
|
2730
|
+
fig.subplots_adjust(left=0.125, right=0.9, top=0.88, bottom=0.11)
|
|
2731
|
+
except Exception:
|
|
2732
|
+
pass
|
|
2573
2733
|
|
|
2574
2734
|
y_data_list = []
|
|
2575
2735
|
x_data_list = []
|
|
@@ -2623,7 +2783,8 @@ def batplot_main() -> int:
|
|
|
2623
2783
|
elif any_txt:
|
|
2624
2784
|
# .txt is generic, require --xaxis
|
|
2625
2785
|
if args.xaxis:
|
|
2626
|
-
|
|
2786
|
+
# Normalize case: 'q' or 'Q' → 'Q' (uppercase), everything else lowercase
|
|
2787
|
+
axis_mode = "Q" if args.xaxis.upper() == "Q" else args.xaxis.lower()
|
|
2627
2788
|
else:
|
|
2628
2789
|
raise ValueError("Unknown file type. Use: batplot file.txt --xaxis [Q|2theta|r|k|energy|rft] or batplot -h for help.")
|
|
2629
2790
|
elif any_lambda or any_cif:
|
|
@@ -2634,7 +2795,8 @@ def batplot_main() -> int:
|
|
|
2634
2795
|
# CIF files are in Q space
|
|
2635
2796
|
axis_mode = "Q"
|
|
2636
2797
|
elif args.xaxis:
|
|
2637
|
-
|
|
2798
|
+
# Normalize case: 'q' or 'Q' → 'Q' (uppercase), everything else lowercase
|
|
2799
|
+
axis_mode = "Q" if args.xaxis.upper() == "Q" else args.xaxis.lower()
|
|
2638
2800
|
else:
|
|
2639
2801
|
raise ValueError("Unknown file type. Use: batplot file.csv --xaxis [Q|2theta|r|k|energy|rft] or batplot -h for help.")
|
|
2640
2802
|
|
|
@@ -2948,6 +3110,19 @@ def batplot_main() -> int:
|
|
|
2948
3110
|
# ---- Store full (converted) arrays BEFORE cropping ----
|
|
2949
3111
|
x_full = x_plot.copy()
|
|
2950
3112
|
y_full_raw = y.copy()
|
|
3113
|
+
|
|
3114
|
+
# ---- Calculate first derivative if requested ----
|
|
3115
|
+
if getattr(args, 'derivative_1d', False) or getattr(args, 'derivative_2d', False):
|
|
3116
|
+
# Calculate dy/dx using numpy gradient
|
|
3117
|
+
# numpy.gradient handles non-uniform spacing automatically
|
|
3118
|
+
if len(y_full_raw) > 1:
|
|
3119
|
+
dy_dx = np.gradient(y_full_raw, x_full)
|
|
3120
|
+
y_full_raw = dy_dx
|
|
3121
|
+
else:
|
|
3122
|
+
# Single point or empty - cannot calculate derivative
|
|
3123
|
+
print(f"Warning: Cannot calculate derivative for {fname}: insufficient data points")
|
|
3124
|
+
continue
|
|
3125
|
+
|
|
2951
3126
|
raw_y_full_list.append(y_full_raw)
|
|
2952
3127
|
x_full_list.append(x_full)
|
|
2953
3128
|
|
|
@@ -3181,13 +3356,20 @@ def batplot_main() -> int:
|
|
|
3181
3356
|
def draw_cif_ticks():
|
|
3182
3357
|
if not cif_tick_series:
|
|
3183
3358
|
return
|
|
3184
|
-
# Preserve
|
|
3359
|
+
# Preserve current limits before drawing - use actual current limits
|
|
3360
|
+
# to prevent any movement when toggling
|
|
3185
3361
|
prev_xlim = ax.get_xlim()
|
|
3186
3362
|
prev_ylim = ax.get_ylim()
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3363
|
+
|
|
3364
|
+
# Store initial limits as fixed reference point to prevent incremental movement
|
|
3365
|
+
# This ensures that repeated 'z' commands don't cause drift
|
|
3366
|
+
# Only set once on first call, then reuse to prevent drift
|
|
3367
|
+
if not hasattr(ax, '_cif_initial_ylim'):
|
|
3368
|
+
ax._cif_initial_ylim = tuple(prev_ylim)
|
|
3369
|
+
fixed_ylim = ax._cif_initial_ylim
|
|
3370
|
+
fixed_yr = fixed_ylim[1] - fixed_ylim[0]
|
|
3371
|
+
if fixed_yr <= 0: fixed_yr = 1.0
|
|
3372
|
+
|
|
3191
3373
|
# Check visibility flag first to decide if we need to adjust y-axis
|
|
3192
3374
|
show_titles = show_cif_titles # Use closure variable
|
|
3193
3375
|
try:
|
|
@@ -3200,38 +3382,50 @@ def batplot_main() -> int:
|
|
|
3200
3382
|
show_titles = bool(getattr(fig, '_bp_show_cif_titles', True))
|
|
3201
3383
|
except Exception:
|
|
3202
3384
|
pass
|
|
3203
|
-
|
|
3385
|
+
|
|
3386
|
+
# Calculate base and spacing based on FIXED y-axis limits (not current)
|
|
3387
|
+
# This prevents incremental movement when toggling
|
|
3204
3388
|
if args.stack or len(y_data_list) > 1:
|
|
3205
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else
|
|
3206
|
-
base = global_min - 0.08*
|
|
3389
|
+
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else fixed_ylim[0]
|
|
3390
|
+
base = global_min - 0.08*fixed_yr; spacing = 0.05*fixed_yr
|
|
3207
3391
|
else:
|
|
3208
3392
|
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else 0.0
|
|
3209
|
-
base = global_min - 0.06*
|
|
3393
|
+
base = global_min - 0.06*fixed_yr; spacing = 0.04*fixed_yr
|
|
3394
|
+
|
|
3210
3395
|
# Only adjust y-axis limits if titles are visible
|
|
3211
|
-
needed_min = base - (len(cif_tick_series)-1)*spacing - 0.04*
|
|
3396
|
+
needed_min = base - (len(cif_tick_series)-1)*spacing - 0.04*fixed_yr
|
|
3397
|
+
if show_titles and needed_min < fixed_ylim[0]:
|
|
3398
|
+
# Expand y-axis only if needed, using fixed limits as reference
|
|
3399
|
+
ax.set_ylim(needed_min, fixed_ylim[1])
|
|
3400
|
+
else:
|
|
3401
|
+
# Restore to fixed limits if no expansion needed
|
|
3402
|
+
ax.set_ylim(fixed_ylim)
|
|
3403
|
+
|
|
3404
|
+
# Get current limits for drawing (after potential expansion)
|
|
3212
3405
|
cur_ylim = ax.get_ylim()
|
|
3213
3406
|
yr = cur_ylim[1] - cur_ylim[0]
|
|
3214
3407
|
if yr <= 0: yr = 1.0
|
|
3215
|
-
if show_titles and needed_min < orig_ylim[0]:
|
|
3216
|
-
# Expand y-axis only if needed, using original limits as reference
|
|
3217
|
-
ax.set_ylim(needed_min, orig_ylim[1])
|
|
3218
|
-
cur_ylim = ax.get_ylim()
|
|
3219
|
-
yr = cur_ylim[1] - cur_ylim[0]
|
|
3220
|
-
if yr <= 0: yr = 1.0
|
|
3221
|
-
# Recalculate base with new limits if we expanded
|
|
3222
|
-
if args.stack or len(y_data_list) > 1:
|
|
3223
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else cur_ylim[0]
|
|
3224
|
-
base = global_min - 0.08*yr; spacing = 0.05*yr
|
|
3225
|
-
else:
|
|
3226
|
-
global_min = min(float(a.min()) for a in y_data_list if len(a)) if y_data_list else 0.0
|
|
3227
|
-
base = global_min - 0.06*yr; spacing = 0.04*yr
|
|
3228
3408
|
# Clear previous
|
|
3229
3409
|
for art in getattr(ax, '_cif_tick_art', []):
|
|
3230
3410
|
try: art.remove()
|
|
3231
3411
|
except Exception: pass
|
|
3232
3412
|
new_art = []
|
|
3233
3413
|
mixed_mode = (not cif_only) # cif_only variable defined earlier in script context
|
|
3234
|
-
|
|
3414
|
+
# Check hkl visibility - check __main__ module first (where interactive menu stores it)
|
|
3415
|
+
# then fall back to closure variable
|
|
3416
|
+
show_hkl = False
|
|
3417
|
+
try:
|
|
3418
|
+
_bp_module = sys.modules.get('__main__')
|
|
3419
|
+
if _bp_module is not None and hasattr(_bp_module, 'show_cif_hkl'):
|
|
3420
|
+
show_hkl = bool(getattr(_bp_module, 'show_cif_hkl', False))
|
|
3421
|
+
except Exception:
|
|
3422
|
+
pass
|
|
3423
|
+
# Fall back to closure variable if not found in module
|
|
3424
|
+
if not show_hkl:
|
|
3425
|
+
try:
|
|
3426
|
+
show_hkl = bool(globals().get('show_cif_hkl', False))
|
|
3427
|
+
except Exception:
|
|
3428
|
+
pass
|
|
3235
3429
|
for i,(lab, fname, peaksQ, wl, qmax_sim, color) in enumerate(cif_tick_series):
|
|
3236
3430
|
y_line = base - i*spacing
|
|
3237
3431
|
if use_2th:
|
|
@@ -3253,14 +3447,12 @@ def batplot_main() -> int:
|
|
|
3253
3447
|
ha='left', va='bottom', fontsize=max(8,int(0.55*plt.rcParams.get('font.size',12))), color=color)
|
|
3254
3448
|
new_art.append(txt)
|
|
3255
3449
|
continue
|
|
3256
|
-
# Build map for quick hkl lookup by Q
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
for qval,h,k,l in hkl_entries:
|
|
3261
|
-
hkl_by_q.setdefault(qval, []).append((h,k,l))
|
|
3262
|
-
label_map = cif_hkl_label_map.get(fname, {})
|
|
3450
|
+
# Build map for quick hkl lookup by Q (only if hkl labels are enabled)
|
|
3451
|
+
label_map = {}
|
|
3452
|
+
if show_hkl:
|
|
3453
|
+
label_map = cif_hkl_label_map.get(fname, {})
|
|
3263
3454
|
# --- Optimized tick & hkl label drawing ---
|
|
3455
|
+
# Check if we should show hkl labels: need show_hkl, peaks, AND a non-empty label_map
|
|
3264
3456
|
if show_hkl and peaksQ and label_map:
|
|
3265
3457
|
# Guard against pathological large peak lists (can freeze UI)
|
|
3266
3458
|
if len(peaksQ) > 4000 or len(domain_peaks) > 4000:
|
|
@@ -3283,7 +3475,8 @@ def batplot_main() -> int:
|
|
|
3283
3475
|
Qp = 4*np.pi*np.sin(theta)/wl
|
|
3284
3476
|
else:
|
|
3285
3477
|
Qp = p
|
|
3286
|
-
|
|
3478
|
+
Qp_rounded = round(Qp, 6)
|
|
3479
|
+
lbl = label_map.get(Qp_rounded)
|
|
3287
3480
|
if lbl:
|
|
3288
3481
|
t_hkl = ax.text(p, y_line+0.022*yr, lbl, ha='center', va='bottom', fontsize=7, rotation=90, color=color)
|
|
3289
3482
|
new_art.append(t_hkl)
|
|
@@ -3309,7 +3502,11 @@ def batplot_main() -> int:
|
|
|
3309
3502
|
elif needed_min >= prev_ylim[0]:
|
|
3310
3503
|
# Titles shown but no expansion needed: restore original limits
|
|
3311
3504
|
ax.set_ylim(prev_ylim)
|
|
3312
|
-
|
|
3505
|
+
else:
|
|
3506
|
+
# Expansion needed: use the minimum of needed_min and prev_ylim[0] to prevent incremental growth
|
|
3507
|
+
# This ensures that repeated toggles don't cause drift
|
|
3508
|
+
new_ymin = min(needed_min, prev_ylim[0])
|
|
3509
|
+
ax.set_ylim(new_ymin, prev_ylim[1])
|
|
3313
3510
|
# Store simplified metadata for hover: list of dicts with 'x','y','label'
|
|
3314
3511
|
hover_meta = []
|
|
3315
3512
|
show_hkl = globals().get('show_cif_hkl', False)
|
|
@@ -3390,19 +3587,36 @@ def batplot_main() -> int:
|
|
|
3390
3587
|
ax._cif_hover_cid = cid
|
|
3391
3588
|
|
|
3392
3589
|
if cif_tick_series:
|
|
3393
|
-
# Auto-assign distinct colors
|
|
3590
|
+
# Auto-assign distinct colors for CIF tick series.
|
|
3591
|
+
# For multiple CIF series:
|
|
3592
|
+
# - If <= 10 files, use 'tab10' but in a re-ordered sequence to
|
|
3593
|
+
# maximize visual separation between adjacent colors.
|
|
3594
|
+
# - If > 10 files, use 'viridis' with evenly spaced samples.
|
|
3595
|
+
#
|
|
3596
|
+
# This overrides any previous per-series color so that the requested
|
|
3597
|
+
# colormap behavior is always enforced.
|
|
3394
3598
|
if len(cif_tick_series) > 1:
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3599
|
+
try:
|
|
3600
|
+
n_cif = len(cif_tick_series)
|
|
3601
|
+
if n_cif <= 10:
|
|
3602
|
+
tab10 = plt.get_cmap('tab10').colors
|
|
3603
|
+
# Reorder indices for more distinct neighboring colors
|
|
3604
|
+
order = [0, 3, 6, 1, 4, 7, 2, 5, 8, 9]
|
|
3399
3605
|
new_series = []
|
|
3400
|
-
for i,(lab,fname,peaksQ,wl,qmax_sim,col) in enumerate(cif_tick_series):
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3606
|
+
for i, (lab, fname, peaksQ, wl, qmax_sim, col) in enumerate(cif_tick_series):
|
|
3607
|
+
idx = order[i] if i < len(order) else i % len(tab10)
|
|
3608
|
+
color = tab10[idx]
|
|
3609
|
+
new_series.append((lab, fname, peaksQ, wl, qmax_sim, color))
|
|
3610
|
+
else:
|
|
3611
|
+
cmap = plt.get_cmap('viridis')
|
|
3612
|
+
positions = np.linspace(0.0, 1.0, n_cif)
|
|
3613
|
+
new_series = []
|
|
3614
|
+
for (pos, (lab, fname, peaksQ, wl, qmax_sim, col)) in zip(positions, cif_tick_series):
|
|
3615
|
+
color = cmap(pos)
|
|
3616
|
+
new_series.append((lab, fname, peaksQ, wl, qmax_sim, color))
|
|
3617
|
+
cif_tick_series[:] = new_series
|
|
3618
|
+
except Exception:
|
|
3619
|
+
pass
|
|
3406
3620
|
if use_2th:
|
|
3407
3621
|
_ensure_wavelength_for_2theta()
|
|
3408
3622
|
draw_cif_ticks()
|