Coverage for /Users/Newville/Codes/xraylarch/larch/wxxas/xas_dialogs.py: 10%
1368 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1import sys
2import os
3import copy
4import time
5from collections import namedtuple
6from functools import partial
7import numpy as np
8from lmfit import Parameters, minimize, fit_report
9from matplotlib.ticker import FuncFormatter
11import wx
12from wxmplot import PlotPanel
13from xraydb import guess_edge
14from larch import Group, isgroup
15from larch.math import index_of, index_nearest, interp
16from larch.utils.strutils import file2groupname, unique_name
17from larch.io import unixpath
19from larch.wxlib import (GridPanel, BitmapButton, FloatCtrl, FloatSpin,
20 set_color, FloatSpinWithPin, get_icon, SimpleText,
21 Choice, SetTip, Check, Button, HLine, OkCancel,
22 LEFT, pack, plotlabels, ReportFrame, DictFrame,
23 FileCheckList, Font, FONTSIZE, plotlabels,
24 get_zoomlimits, set_zoomlimits)
26from larch.xafs import etok, ktoe, find_energy_step
27from larch.utils.physical_constants import PI, DEG2RAD, PLANCK_HC, ATOM_SYMS
28from larch.math import smooth
30Plot_Choices = {'Normalized': 'norm', 'Derivative': 'dmude'}
33EDGE_LIST = ('K', 'L3', 'L2', 'L1', 'M5', 'M4', 'M3')
35NORM_MU = 'Normalized \u03BC(E)'
37DEGLITCH_PLOTS = {'Raw \u03BC(E)': 'mu',
38 NORM_MU: 'norm',
39 '\u03c7(E)': 'chie',
40 '\u03c7(E)*(E-E_0)': 'chiew'}
42SESSION_PLOTS = {'Normalized \u03BC(E)': 'norm',
43 'Raw \u03BC(E)': 'mu',
44 'k^2\u03c7(k)': 'chikw'}
47def ensure_en_orig(dgroup):
48 if not hasattr(dgroup, 'energy_orig'):
49 dgroup.energy_orig = dgroup.energy[:]
53def fit_dialog_window(dialog, panel):
54 sizer = wx.BoxSizer(wx.VERTICAL)
55 sizer.Add(panel, 1, LEFT, 5)
56 pack(dialog, sizer)
57 dialog.Fit()
58 w0, h0 = dialog.GetSize()
59 w1, h1 = dialog.GetBestSize()
60 dialog.SetSize((max(w0, w1)+25, max(h0, h1)+25))
64def add_floatspin(name, value, panel, with_pin=True, xasmain=None,
65 callback=None, relative_e0=False, **kws):
66 """create FloatSpin with Pin button for onSelPoint"""
67 if with_pin and xasmain is not None:
68 pin_action = partial(xasmain.onSelPoint, opt=name,
69 relative_e0=relative_e0,
70 callback=callback)
71 fspin, pinb = FloatSpinWithPin(panel, value=value,
72 pin_action=pin_action, **kws)
73 else:
74 fspin = FloatSpin(panel, value=value, **kws)
75 pinb = None
76 return fspin, pinb
79def get_view_limits(ppanel):
80 "get last zoom limits for a plot panel"
81 xlim = ppanel.axes.get_xlim()
82 ylim = ppanel.axes.get_ylim()
83 return (xlim, ylim)
85def set_view_limits(ppanel, xlim, ylim):
86 "set zoom limits for a plot panel, as found from get_view_limits"
87 ppanel.axes.set_xlim(xlim, emit=True)
88 ppanel.axes.set_ylim(ylim, emit=True)
91class OverAbsorptionDialog(wx.Dialog):
92 """dialog for correcting over-absorption"""
93 def __init__(self, parent, controller, **kws):
94 self.parent = parent
95 self.controller = controller
96 self.dgroup = self.controller.get_group()
97 groupnames = list(self.controller.file_groups.keys())
99 self.data = [self.dgroup.energy[:], self.dgroup.norm[:]]
101 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
102 title="Correct Over-absorption")
103 self.SetFont(Font(FONTSIZE))
104 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
105 self.wids = wids = {}
107 wids['grouplist'] = Choice(panel, choices=groupnames, size=(250, -1),
108 action=self.on_groupchoice)
110 wids['grouplist'].SetStringSelection(self.dgroup.filename)
112 opts = dict(size=(90, -1), precision=1, act_on_losefocus=True,
113 minval=-90, maxval=180)
115 fs_opts = dict(size=(90, -1), value=45, digits=1, increment=1)
116 wids['phi_in'] = FloatSpin(panel, **fs_opts)
117 wids['phi_out'] = FloatSpin(panel, **fs_opts)
119 wids['elem'] = Choice(panel, choices=ATOM_SYMS[:98], size=(50, -1))
120 wids['edge'] = Choice(panel, choices=EDGE_LIST, size=(50, -1))
122 wids['formula'] = wx.TextCtrl(panel, -1, '', size=(250, -1))
124 self.set_default_elem_edge(self.dgroup)
126 #wids['apply'] = Button(panel, 'Save / Overwrite', size=(150, -1),
127 # action=self.on_apply)
128 #SetTip(wids['apply'], 'Save corrected data, overwrite current arrays')
130 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
131 action=self.on_saveas)
132 SetTip(wids['save_as'], 'Save corrected data as new group')
134 wids['save_as_name'] = wx.TextCtrl(panel, -1, self.dgroup.filename + '_abscorr',
135 size=(250, -1))
136 wids['correct'] = Button(panel, 'Do Correction',
137 size=(150, -1), action=self.on_correct)
138 SetTip(wids['correct'], 'Calculate Correction')
140 def add_text(text, dcol=1, newrow=True):
141 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
143 add_text(' Correction for Group: ', newrow=False)
144 panel.Add(wids['grouplist'], dcol=5)
146 add_text(' Absorbing Element: ')
147 panel.Add(wids['elem'])
149 add_text(' Edge: ', newrow=False)
150 panel.Add(wids['edge'])
152 add_text(' Material Formula: ')
153 panel.Add(wids['formula'], dcol=3)
155 add_text(' Incident Angle (deg): ')
156 panel.Add(wids['phi_in'])
158 add_text(' Exit Angle (deg): ')
159 panel.Add(wids['phi_out'])
161 panel.Add(wids['correct'], newrow=True)
162 # panel.Add(wids['apply'], dcol=2, newrow=True)
164 panel.Add(wids['save_as'], newrow=True)
165 panel.Add(wids['save_as_name'], dcol=3)
166 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
167 newrow=True)
168 panel.pack()
169 fit_dialog_window(self, panel)
170 self.plot_results(keep_limits=False)
173 def onDone(self, event=None):
174 self.Destroy()
176 def set_default_elem_edge(self, dgroup):
177 elem, edge = guess_edge(dgroup.e0)
178 self.wids['elem'].SetStringSelection(elem)
179 self.wids['edge'].SetStringSelection(edge)
181 def on_groupchoice(self, event=None):
182 fname = self.wids['grouplist'].GetStringSelection()
183 self.dgroup = self.controller.get_group(fname)
184 self.set_default_elem_edge(self.dgroup)
185 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_abscorr')
188 def on_correct(self, event=None):
189 wids = self.wids
190 dgroup = self.dgroup
191 anginp = wids['phi_in'].GetValue()
192 angout = wids['phi_out'].GetValue()
193 elem = wids['elem'].GetStringSelection()
194 edge = wids['edge'].GetStringSelection()
195 formula = wids['formula'].GetValue()
196 if len(formula) < 1:
197 return
199 cmd = """fluo_corr(%s.energy, %s.mu, '%s', '%s', edge='%s', group=%s,
200 anginp=%.1f, angout=%.1f)""" % (dgroup.groupname, dgroup.groupname,
201 formula, elem, edge, dgroup.groupname,
202 anginp, angout)
203 self.cmd = cmd
204 self.controller.larch.eval(cmd)
205 self.plot_results()
207 def on_apply(self, event=None):
208 xdat, ydat = self.data
209 dgroup = self.dgroup
210 dgroup.xdat = dgroup.energy = xdat
211 self.parent.process_normalization(dgroup)
212 dgroup.journal.add('fluor_corr_command', self.cmd)
213 self.plot_results()
215 def on_saveas(self, event=None):
216 wids = self.wids
217 fname = self.wids['grouplist'].GetStringSelection()
218 new_fname = wids['save_as_name'].GetValue()
219 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
221 if hasattr(self.dgroup, 'norm_corr' ):
222 ngroup.mu = ngroup.norm_corr*1.0
223 del ngroup.norm_corr
225 ogroup = self.controller.get_group(fname)
226 self.parent.install_group(ngroup, journal=ogroup.journal)
227 olddesc = ogroup.journal.get('source_desc').value
228 ngroup.journal.add('source_desc', f"fluo_corrected({olddesc})")
229 ngroup.journal.add('fluor_correction_command', self.cmd)
231 def plot_results(self, event=None, keep_limits=True):
232 ppanel = self.controller.get_display(stacked=False).panel
234 dgroup = self.dgroup
235 xlim, ylim = get_view_limits(ppanel)
236 path, fname = os.path.split(dgroup.filename)
238 opts = dict(linewidth=3, ylabel=plotlabels.norm,
239 xlabel=plotlabels.energy, delay_draw=True,
240 show_legend=True)
242 if self.controller.plot_erange is not None:
243 opts['xmin'] = dgroup.e0 + self.controller.plot_erange[0]
244 opts['xmax'] = dgroup.e0 + self.controller.plot_erange[1]
246 if not hasattr(dgroup, 'norm_corr'):
247 dgroup.norm_corr = dgroup.norm[:]
249 ppanel.plot(dgroup.energy, dgroup.norm_corr, zorder=10, marker=None,
250 title='Over-absorption Correction:\n %s' % fname,
251 label='corrected', **opts)
253 ppanel.oplot(dgroup.energy, dgroup.norm, zorder=10, marker='o',
254 markersize=3, label='original', **opts)
255 if keep_limits:
256 set_view_limits(ppanel, xlim, ylim)
257 ppanel.canvas.draw()
258 ppanel.conf.draw_legend(show=True)
260 def GetResponse(self):
261 raise AttributeError("use as non-modal dialog!")
264class EnergyCalibrateDialog(wx.Dialog):
265 """dialog for calibrating energy"""
266 def __init__(self, parent, controller, **kws):
268 self.parent = parent
269 self.controller = controller
270 self.dgroup = self.controller.get_group()
271 groupnames = list(self.controller.file_groups.keys())
273 ensure_en_orig(self.dgroup)
275 self.data = [self.dgroup.energy_orig[:], self.dgroup.norm[:]]
276 xmin = min(self.dgroup.energy_orig)
277 xmax = max(self.dgroup.energy_orig)
278 e0val = getattr(self.dgroup, 'e0', xmin)
280 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
281 title="Calibrate / Align Energy")
282 self.SetFont(Font(FONTSIZE))
283 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
285 self.wids = wids = {}
286 wids['grouplist'] = Choice(panel, choices=groupnames, size=(275, -1),
287 action=self.on_groupchoice)
288 wids['grouplist'].SetStringSelection(self.dgroup.filename)
290 refgroups = ['None'] + groupnames
291 wids['reflist'] = Choice(panel, choices=refgroups, size=(275, -1),
292 action=self.on_align, default=0)
294 opts = dict(size=(90, -1), digits=3, increment=0.1)
296 opts['action'] = partial(self.on_calib, name='eshift')
297 wids['eshift'] = FloatSpin(panel, value=0, **opts)
299 self.plottype = Choice(panel, choices=list(Plot_Choices.keys()),
300 size=(275, -1), action=self.plot_results)
301 wids['do_align'] = Button(panel, 'Auto Align', size=(100, -1),
302 action=self.on_align)
304 wids['apply_one'] = Button(panel, 'Apply to Current Group', size=(200, -1),
305 action=self.on_apply_one)
307 wids['apply_sel'] = Button(panel, 'Apply to Selected Groups',
308 size=(250, -1), action=self.on_apply_sel)
309 SetTip(wids['apply_sel'], 'Apply the Energy Shift to all Selected Groups')
311 wids['save_as'] = Button(panel, 'Save As New Group ', size=(200, -1),
312 action=self.on_saveas)
313 SetTip(wids['save_as'], 'Save shifted data as new group')
315 wids['save_as_name'] = wx.TextCtrl(panel, -1,
316 self.dgroup.filename + '_eshift',
317 size=(275, -1))
319 wids['sharedref_msg'] = wx.StaticText(panel, label="1 groups share this energy reference")
320 wids['select_sharedref'] = Button(panel, 'Select Groups with shared reference',
321 size=(300, -1), action=self.on_select_sharedrefs)
323 def add_text(text, dcol=1, newrow=True):
324 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
326 add_text(' Current Group: ', newrow=False)
327 panel.Add(wids['grouplist'], dcol=2)
329 add_text(' Auto-Align to : ')
330 panel.Add(wids['reflist'], dcol=2)
332 add_text(' Plot Arrays as: ')
333 panel.Add(self.plottype, dcol=2)
335 add_text(' Energy Shift (eV): ')
336 panel.Add(wids['eshift'], dcol=1)
337 panel.Add(wids['do_align'], dcol=1)
338 panel.Add(HLine(panel, size=(500, 3)), dcol=4, newrow=True)
339 # panel.Add(apply_one, newrow=True)
341 panel.Add(wids['sharedref_msg'], dcol=2, newrow=True)
342 panel.Add(wids['select_sharedref'], dcol=2)
343 panel.Add(wids['apply_one'], dcol=1, newrow=True)
344 panel.Add(wids['apply_sel'], dcol=2)
346 panel.Add(HLine(panel, size=(500, 3)), dcol=4, newrow=True)
348 panel.Add(wids['save_as'], newrow=True)
349 panel.Add(wids['save_as_name'], dcol=3)
351 panel.pack()
353 fit_dialog_window(self, panel)
355 self.plot_results(keep_limits=False)
356 wx.CallAfter(self.get_groups_shared_energyrefs)
358 def on_select(self, event=None, opt=None):
359 _x, _y = self.controller.get_cursor()
360 if opt in self.wids:
361 self.wids[opt].SetValue(_x)
363 def get_groups_shared_energyrefs(self, dgroup=None):
364 if dgroup is None:
365 dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
366 sharedrefs = [dgroup.filename]
367 try:
368 eref = dgroup.config.xasnorm.get('energy_ref', None)
369 except:
370 eref = None
371 if eref is None:
372 eref = dgroup.groupname
375 for key, val in self.controller.file_groups.items():
376 if dgroup.groupname == val:
377 continue
378 g = self.controller.get_group(val)
379 try:
380 geref = g.config.xasnorm.get('energy_ref', None)
381 except:
382 geref = None
383 # print(key, val, geref, geref == ref_filename)
384 if geref == eref or geref == dgroup.filename:
385 sharedrefs.append(key)
386 self.wids['sharedref_msg'].SetLabel(f" {len(sharedrefs):d} groups share this energy reference")
387 return sharedrefs
389 def on_select_sharedrefs(self, event=None):
390 groups = self.get_groups_shared_energyrefs()
391 self.controller.filelist.SetCheckedStrings(groups)
393 def on_groupchoice(self, event=None):
394 dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
395 self.dgroup = dgroup
396 others = self.get_groups_shared_energyrefs(dgroup)
397 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_eshift')
398 self.plot_results()
400 def on_align(self, event=None, name=None, value=None):
401 ref = self.controller.get_group(self.wids['reflist'].GetStringSelection())
402 dat = self.dgroup
403 ensure_en_orig(dat)
404 ensure_en_orig(ref)
406 dat.xdat = dat.energy_orig[:]
407 ref.xdat = ref.energy_orig[:]
408 estep = find_energy_step(dat.xdat)
409 i1 = index_of(ref.energy_orig, ref.e0-20)
410 i2 = index_of(ref.energy_orig, ref.e0+20)
412 def resid(pars, ref, dat, i1, i2):
413 "fit residual"
414 newx = dat.xdat + pars['eshift'].value
415 scale = pars['scale'].value
416 y = interp(newx, dat.dmude, ref.xdat, kind='cubic')
417 return smooth(ref.xdat, y*scale-ref.dmude, xstep=estep, sigma=0.50)[i1:i2]
419 params = Parameters()
420 params.add('eshift', value=ref.e0-dat.e0, min=-50, max=50)
421 params.add('scale', value=1, min=0, max=50)
423 result = minimize(resid, params, args=(ref, dat, i1, i2))
424 eshift = result.params['eshift'].value
425 self.wids['eshift'].SetValue(eshift)
427 ensure_en_orig(self.dgroup)
428 xnew = self.dgroup.energy_orig + eshift
429 self.data = xnew, self.dgroup.norm[:]
430 self.plot_results()
432 def on_calib(self, event=None, name=None):
433 wids = self.wids
434 eshift = wids['eshift'].GetValue()
435 ensure_en_orig(self.dgroup)
436 xnew = self.dgroup.energy_orig + eshift
437 self.data = xnew, self.dgroup.norm[:]
438 self.plot_results()
440 def on_apply_one(self, event=None):
441 xdat, ydat = self.data
442 dgroup = self.dgroup
443 eshift = self.wids['eshift'].GetValue()
445 ensure_en_orig(dgroup)
447 idx, norm_page = self.parent.get_nbpage('norm')
448 norm_page.wids['energy_shift'].SetValue(eshift)
450 dgroup.energy_shift = eshift
451 dgroup.xdat = dgroup.energy = eshift + dgroup.energy_orig[:]
452 dgroup.journal.add('energy_shift ', eshift)
453 self.parent.process_normalization(dgroup)
454 self.plot_results()
456 def on_apply_sel(self, event=None):
457 eshift = self.wids['eshift'].GetValue()
458 idx, norm_page = self.parent.get_nbpage('norm')
459 for checked in self.controller.filelist.GetCheckedStrings():
460 fname = self.controller.file_groups[str(checked)]
461 dgroup = self.controller.get_group(fname)
462 ensure_en_orig(dgroup)
463 dgroup.energy_shift = eshift
464 norm_page.wids['energy_shift'].SetValue(eshift)
466 dgroup.xdat = dgroup.energy = eshift + dgroup.energy_orig[:]
467 dgroup.journal.add('energy_shift ', eshift)
468 self.parent.process_normalization(dgroup)
470 def on_saveas(self, event=None):
471 wids = self.wids
472 fname = wids['grouplist'].GetStringSelection()
473 eshift = wids['eshift'].GetValue()
474 new_fname = wids['save_as_name'].GetValue()
475 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
477 ensure_en_orig(ngroup)
478 ngroup.xdat = ngroup.energy = eshift + ngroup.energy_orig[:]
479 ngroup.energy_shift = 0
480 ngroup.energy_ref = ngroup.groupname
482 ogroup = self.controller.get_group(fname)
483 self.parent.install_group(ngroup, journal=ogroup.journal)
484 olddesc = ogroup.journal.get('source_desc').value
485 ngroup.journal.add('source_desc', f"energy_shifted({olddesc}, {eshift:.4f})")
486 ngroup.journal.add('energy_shift ', 0.0)
488 def plot_results(self, event=None, keep_limits=True):
489 ppanel = self.controller.get_display(stacked=False).panel
490 ppanel.oplot
491 xnew, ynew = self.data
492 dgroup = self.dgroup
494 xlim, ylim = get_view_limits(ppanel)
495 path, fname = os.path.split(dgroup.filename)
497 wids = self.wids
498 eshift = wids['eshift'].GetValue()
499 e0_old = dgroup.e0
500 e0_new = dgroup.e0 + eshift
502 xmin = min(e0_old, e0_new) - 25
503 xmax = max(e0_old, e0_new) + 50
505 use_deriv = self.plottype.GetStringSelection().lower().startswith('deriv')
507 ylabel = plotlabels.norm
508 if use_deriv:
509 ynew = np.gradient(ynew)/np.gradient(xnew)
510 ylabel = plotlabels.dmude
512 opts = dict(xmin=xmin, xmax=xmax, ylabel=ylabel, delay_draw=True,
513 xlabel=plotlabels.energy, show_legend=True)
515 if self.controller.plot_erange is not None:
516 opts['xmin'] = dgroup.e0 + self.controller.plot_erange[0]
517 opts['xmax'] = dgroup.e0 + self.controller.plot_erange[1]
519 xold, yold = self.dgroup.energy_orig, self.dgroup.norm
520 if use_deriv:
521 yold = np.gradient(yold)/np.gradient(xold)
523 ppanel.plot(xold, yold, zorder=10, marker='o', markersize=3,
524 label='original', linewidth=2, color='#1f77b4',
525 title=f'Energy Calibration:\n {fname}', **opts)
527 ppanel.oplot(xnew, ynew, zorder=15, marker='+', markersize=3,
528 linewidth=2, label='shifted',
529 color='#d62728', **opts)
531 if wids['reflist'].GetStringSelection() != 'None':
532 refgroup = self.controller.get_group(wids['reflist'].GetStringSelection())
533 xref, yref = refgroup.energy, refgroup.norm
534 if use_deriv:
535 yref = np.gradient(yref)/np.gradient(xref)
537 ppanel.oplot(xref, yref, style='solid', zorder=5, color='#2ca02c',
538 marker=None, label=refgroup.filename, **opts)
539 if keep_limits:
540 set_view_limits(ppanel, xlim, ylim)
541 ppanel.canvas.draw()
544 def GetResponse(self):
545 raise AttributeError("use as non-modal dialog!")
547class RebinDataDialog(wx.Dialog):
548 """dialog for rebinning data to standard XAFS grid"""
549 def __init__(self, parent, controller, **kws):
551 self.parent = parent
552 self.controller = controller
553 self.dgroup = self.controller.get_group()
554 groupnames = list(self.controller.file_groups.keys())
556 xmin = min(self.dgroup.energy)
557 xmax = max(self.dgroup.energy)
558 e0val = getattr(self.dgroup, 'e0', xmin)
559 xanes_step = 0.05 * (1 + max(1, int(e0val / 2000.0)))
560 self.data = [self.dgroup.energy[:], self.dgroup.mu[:],
561 self.dgroup.mu*0, e0val]
563 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
564 title="Rebin mu(E) Data")
565 self.SetFont(Font(FONTSIZE))
566 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
568 self.wids = wids = {}
570 wids['grouplist'] = Choice(panel, choices=groupnames, size=(250, -1),
571 action=self.on_groupchoice)
573 wids['grouplist'].SetStringSelection(self.dgroup.groupname)
575 opts = dict(size=(90, -1), precision=3, act_on_losefocus=True)
577 wids['e0'] = FloatCtrl(panel, value=e0val, minval=xmin, maxval=xmax, **opts)
578 pre1 = 10.0*(1+int((xmin-e0val)/10.0))
579 wids['pre1'] = FloatCtrl(panel, value=pre1, **opts)
580 wids['pre2'] = FloatCtrl(panel, value=-15, **opts)
581 wids['xanes1'] = FloatCtrl(panel, value=-15, **opts)
582 wids['xanes2'] = FloatCtrl(panel, value=15, **opts)
583 wids['exafs1'] = FloatCtrl(panel, value=etok(15), **opts)
584 wids['exafs2'] = FloatCtrl(panel, value=etok(xmax-e0val), **opts)
586 wids['pre_step'] = FloatCtrl(panel, value=2.0, **opts)
587 wids['xanes_step'] = FloatCtrl(panel, value=xanes_step, **opts)
588 wids['exafs_step'] = FloatCtrl(panel, value=0.05, **opts)
590 for wname, wid in wids.items():
591 if wname != 'grouplist':
592 wid.SetAction(partial(self.on_rebin, name=wname))
594 #wids['apply'] = Button(panel, 'Save / Overwrite', size=(150, -1),
595 # action=self.on_apply)
596 #SetTip(wids['apply'], 'Save rebinned data, overwrite current arrays')
598 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
599 action=self.on_saveas)
600 SetTip(wids['save_as'], 'Save corrected data as new group')
602 wids['save_as_name'] = wx.TextCtrl(panel, -1, self.dgroup.filename + '_rebin',
603 size=(250, -1))
605 def add_text(text, dcol=1, newrow=True):
606 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
608 add_text('Rebin Data for Group: ', dcol=2, newrow=False)
609 panel.Add(wids['grouplist'], dcol=3)
611 add_text('E0: ')
612 panel.Add(wids['e0'])
613 add_text(' eV', newrow=False)
615 add_text('Region ')
616 add_text('Start ', newrow=False)
617 add_text('Stop ', newrow=False)
618 add_text('Step ', newrow=False)
619 add_text('Units ', newrow=False)
621 add_text('Pre-Edge: ')
622 panel.Add(wids['pre1'])
623 panel.Add(wids['pre2'])
624 panel.Add(wids['pre_step'])
625 add_text(' eV', newrow=False)
627 add_text('XANES: ')
628 panel.Add(wids['xanes1'])
629 panel.Add(wids['xanes2'])
630 panel.Add(wids['xanes_step'])
631 add_text(' eV', newrow=False)
633 add_text('EXAFS: ')
634 panel.Add(wids['exafs1'])
635 panel.Add(wids['exafs2'])
636 panel.Add(wids['exafs_step'])
637 add_text('1/\u212B', newrow=False)
639 # panel.Add(wids['apply'], dcol=2, newrow=True)
640 panel.Add(wids['save_as'], dcol=2, newrow=True)
641 panel.Add(wids['save_as_name'], dcol=3)
642 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
643 newrow=True)
644 panel.pack()
646 fit_dialog_window(self, panel)
648 self.on_rebin()
649 self.plot_results(keep_limits=False)
651 def onDone(self, event=None):
652 self.Destroy()
654 def on_groupchoice(self, event=None):
655 self.dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
656 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_rebin')
657 self.plot_results()
659 def on_rebin(self, event=None, name=None, value=None):
660 wids = self.wids
661 if name == 'pre2':
662 val = wids['pre2'].GetValue()
663 wids['xanes1'].SetValue(val, act=False)
664 elif name == 'xanes1':
665 val = wids['xanes1'].GetValue()
666 wids['pre2'].SetValue(val, act=False)
667 elif name == 'xanes2':
668 val = wids['xanes2'].GetValue()
669 wids['exafs1'].SetValue(etok(val), act=False)
670 elif name == 'exafs1':
671 val = wids['exafs1'].GetValue()
672 wids['xanes2'].SetValue(ktoe(val), act=False)
674 e0 = wids['e0'].GetValue()
675 args = dict(group=self.dgroup.groupname, e0=e0,
676 pre1=wids['pre1'].GetValue(),
677 pre2=wids['pre2'].GetValue(),
678 pre_step=wids['pre_step'].GetValue(),
679 exafs1=ktoe(wids['exafs1'].GetValue()),
680 exafs2=ktoe(wids['exafs2'].GetValue()),
681 exafs_kstep=wids['exafs_step'].GetValue(),
682 xanes_step=wids['xanes_step'].GetValue())
684 # do rebin:
685 cmd = """rebin_xafs({group}, e0={e0:f}, pre1={pre1:f}, pre2={pre2:f},
686 pre_step={pre_step:f}, xanes_step={xanes_step:f}, exafs1={exafs1:f},
687 exafs2={exafs2:f}, exafs_kstep={exafs_kstep:f})""".format(**args)
688 self.cmd = cmd
689 self.controller.larch.eval(cmd)
691 if hasattr(self.dgroup, 'rebinned'):
692 xnew = self.dgroup.rebinned.energy
693 ynew = self.dgroup.rebinned.mu
694 yerr = self.dgroup.rebinned.delta_mu
695 self.data = xnew, ynew, yerr, e0
696 self.plot_results()
698 def on_apply(self, event=None):
699 xdat, ydat, yerr, e0 = self.data
700 dgroup = self.dgroup
701 dgroup.energy = dgroup.xdat = xdat
702 dgroup.mu = dgroup.ydat = ydat
703 dgroup.journal.add('rebin_command ', self.cmd)
704 self.parent.process_normalization(dgroup)
705 self.plot_results()
707 def on_saveas(self, event=None):
708 wids = self.wids
709 fname = wids['grouplist'].GetStringSelection()
710 new_fname = wids['save_as_name'].GetValue()
711 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
712 xdat, ydat, yerr, de0 = self.data
713 ngroup.energy = ngroup.xdat = xdat
714 ngroup.mu = ngroup.ydat = ydat
716 ogroup = self.controller.get_group(fname)
717 olddesc = ogroup.journal.get('source_desc').value
719 ngroup.delta_mu = getattr(ngroup, 'yerr', 1.0)
720 self.parent.process_normalization(ngroup)
722 self.parent.install_group(ngroup, journal=ogroup.journal)
723 ngroup.journal.add('source_desc', f"rebinned({olddesc})")
724 ngroup.journal.add('rebin_command ', self.cmd)
726 def on_done(self, event=None):
727 self.Destroy()
729 def plot_results(self, event=None, keep_limits=True):
730 ppanel = self.controller.get_display(stacked=False).panel
731 xnew, ynew, yerr, e0 = self.data
732 dgroup = self.dgroup
733 xlim, ylim = get_view_limits(ppanel)
734 path, fname = os.path.split(dgroup.filename)
736 opts = {'delay_draw': True}
737 if self.controller.plot_erange is not None:
738 opts['xmin'] = dgroup.e0 + self.controller.plot_erange[0]
739 opts['xmax'] = dgroup.e0 + self.controller.plot_erange[1]
741 ppanel.plot(xnew, ynew, zorder=20, marker='square',
742 linewidth=3, title='Enegy rebinning:\n %s' % fname,
743 label='rebinned', xlabel=plotlabels.energy,
744 ylabel=plotlabels.mu, **opts)
746 xold, yold = self.dgroup.energy, self.dgroup.mu
747 ppanel.oplot(xold, yold, zorder=10,
748 marker='o', markersize=4, linewidth=2.0,
749 label='original', show_legend=True, **opts)
750 if keep_limits:
751 set_view_limits(ppanel, xlim, ylim)
752 ppanel.canvas.draw()
754 def GetResponse(self):
755 raise AttributeError("use as non-modal dialog!")
757class SmoothDataDialog(wx.Dialog):
758 """dialog for smoothing data"""
759 def __init__(self, parent, controller, **kws):
761 self.parent = parent
762 self.controller = controller
763 self.dgroup = self.controller.get_group()
764 groupnames = list(self.controller.file_groups.keys())
766 self.data = [self.dgroup.energy[:], self.dgroup.mu[:]]
769 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
770 title="Smooth mu(E) Data")
771 self.SetFont(Font(FONTSIZE))
772 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
774 self.wids = wids = {}
776 wids['grouplist'] = Choice(panel, choices=groupnames, size=(250, -1),
777 action=self.on_groupchoice)
779 wids['grouplist'].SetStringSelection(self.dgroup.filename)
780 SetTip(wids['grouplist'], 'select a new group, clear undo history')
782 smooth_ops = ('None', 'Boxcar', 'Savitzky-Golay', 'Convolution')
783 conv_ops = ('Lorenztian', 'Gaussian')
785 self.smooth_op = Choice(panel, choices=smooth_ops, size=(150, -1),
786 action=self.on_smooth)
787 self.smooth_op.SetSelection(0)
789 self.conv_op = Choice(panel, choices=conv_ops, size=(150, -1),
790 action=self.on_smooth)
791 self.conv_op.SetSelection(0)
793 opts = dict(size=(50, -1), act_on_losefocus=True, odd_only=False)
795 self.sigma = FloatSpin(panel, value=1, digits=2, min_val=0, increment=0.1,
796 size=(60, -1), action=self.on_smooth)
798 self.par_n = FloatSpin(panel, value=2, digits=0, min_val=1, increment=1,
799 size=(60, -1), action=self.on_smooth)
801 self.par_o = FloatSpin(panel, value=1, digits=0, min_val=1, increment=1,
802 size=(60, -1), action=self.on_smooth)
804 # for fc in (self.sigma, self.par_n, self.par_o):
805 # self.sigma.SetAction(self.on_smooth)
807 self.message = SimpleText(panel, label=' ', size=(200, -1))
810 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
811 action=self.on_saveas)
812 SetTip(wids['save_as'], 'Save corrected data as new group')
814 wids['save_as_name'] = wx.TextCtrl(panel, -1, self.dgroup.filename + '_smooth',
815 size=(250, -1))
817 def add_text(text, dcol=1, newrow=True):
818 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
820 add_text('Smooth Data for Group: ', newrow=False)
821 panel.Add(wids['grouplist'], dcol=5)
823 add_text('Smoothing Method: ')
824 panel.Add(self.smooth_op)
825 add_text(' n = ', newrow=False)
826 panel.Add(self.par_n)
827 add_text(' order= ', newrow=False)
828 panel.Add(self.par_o)
830 add_text('Convolution Form: ')
831 panel.Add(self.conv_op)
832 add_text(' sigma: ', newrow=False)
833 panel.Add(self.sigma)
835 panel.Add((10, 10), newrow=True)
836 panel.Add(self.message, dcol=5)
838 # panel.Add(wids['apply'], newrow=True)
840 panel.Add(wids['save_as'], newrow=True)
841 panel.Add(wids['save_as_name'], dcol=5)
842 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
843 newrow=True)
844 panel.pack()
845 fit_dialog_window(self, panel)
847 self.plot_results(keep_limits=False)
849 def onDone(self, event=None):
850 self.Destroy()
853 def on_groupchoice(self, event=None):
854 self.dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
855 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_smooth')
856 self.plot_results()
858 def on_smooth(self, event=None, value=None):
859 smoothop = self.smooth_op.GetStringSelection().lower()
861 convop = self.conv_op.GetStringSelection()
862 self.conv_op.Enable(smoothop.startswith('conv'))
863 self.sigma.Enable(smoothop.startswith('conv'))
864 self.message.SetLabel('')
865 self.par_n.SetMin(1)
866 self.par_n.odd_only = False
867 par_n = int(self.par_n.GetValue())
868 par_o = int(self.par_o.GetValue())
869 sigma = self.sigma.GetValue()
870 cmd = '{group:s}.mu' # No smoothing
871 estep = find_energy_step(self.data[0])
872 if smoothop.startswith('box'):
873 self.par_n.Enable()
874 cmd = "boxcar({group:s}.mu, {par_n:d})"
875 self.conv_op.Disable()
876 elif smoothop.startswith('savi'):
877 self.par_n.Enable()
878 self.par_n.odd_only = True
879 self.par_o.Enable()
881 x0 = max(par_o + 1, par_n)
882 if x0 % 2 == 0:
883 x0 += 1
884 self.par_n.SetMin(par_o + 1)
885 if par_n != x0:
886 self.par_n.SetValue(x0)
887 self.message.SetLabel('n must odd and > order+1')
889 cmd = "savitzky_golay({group:s}.mu, {par_n:d}, {par_o:d})"
891 elif smoothop.startswith('conv'):
892 cmd = "smooth({group:s}.energy, {group:s}.mu, xstep={estep:f}, sigma={sigma:f}, form='{convop:s}')"
893 self.cmd = cmd.format(group=self.dgroup.groupname, convop=convop,
894 estep=estep, sigma=sigma, par_n=par_n, par_o=par_o)
896 self.controller.larch.eval("_tmpy = %s" % self.cmd)
897 self.data = self.dgroup.energy[:], self.controller.symtable._tmpy
898 self.plot_results()
900 def on_apply(self, event=None):
901 xdat, ydat = self.data
902 dgroup = self.dgroup
903 dgroup.energy = xdat
904 dgroup.mu = ydat
905 dgroup.journal.add('smooth_command', self.cmd)
906 self.parent.process_normalization(dgroup)
907 self.plot_results()
909 def on_saveas(self, event=None):
910 wids = self.wids
911 fname = wids['grouplist'].GetStringSelection()
912 new_fname = wids['save_as_name'].GetValue()
913 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
915 xdat, ydat = self.data
916 ngroup.energy = ngroup.xdat = xdat
917 ngroup.mu = ngroup.ydat = ydat
919 ogroup = self.controller.get_group(fname)
920 olddesc = ogroup.journal.get('source_desc').value
922 self.parent.install_group(ngroup, journal=ogroup.journal)
923 ngroup.journal.add('source_desc', f"smoothed({olddesc})")
924 ngroup.journal.add('smooth_command', self.cmd)
925 self.parent.process_normalization(ngroup)
927 def on_done(self, event=None):
928 self.Destroy()
930 def plot_results(self, event=None, keep_limits=True):
931 ppanel = self.controller.get_display(stacked=False).panel
932 xnew, ynew = self.data
933 dgroup = self.dgroup
934 path, fname = os.path.split(dgroup.filename)
935 opts = {'delay_draw': True}
936 xlim, ylim = get_view_limits(ppanel)
938 if self.controller.plot_erange is not None:
939 opts['xmin'] = dgroup.e0 + self.controller.plot_erange[0]
940 opts['xmax'] = dgroup.e0 + self.controller.plot_erange[1]
942 ppanel.plot(xnew, ynew, zorder=20, marker=None,
943 linewidth=3, title='Smoothing:\n %s' % fname,
944 label='smoothed', xlabel=plotlabels.energy,
945 ylabel=plotlabels.mu, **opts)
947 xold, yold = self.dgroup.energy, self.dgroup.mu
948 ppanel.oplot(xold, yold, zorder=10,
949 marker='o', markersize=4, linewidth=2.0,
950 label='original', show_legend=True, **opts)
951 if keep_limits:
952 set_view_limits(ppanel, xlim, ylim)
953 ppanel.canvas.draw()
955 def GetResponse(self):
956 raise AttributeError("use as non-modal dialog!")
958class DeconvolutionDialog(wx.Dialog):
959 """dialog for energy deconvolution"""
960 def __init__(self, parent, controller, **kws):
962 self.parent = parent
963 self.controller = controller
964 self.dgroup = self.controller.get_group()
965 groupnames = list(self.controller.file_groups.keys())
967 self.data = [self.dgroup.energy[:], self.dgroup.norm[:]]
970 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
971 title="Deconvolve mu(E) Data")
972 self.SetFont(Font(FONTSIZE))
973 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
975 self.wids = wids = {}
977 wids['grouplist'] = Choice(panel, choices=groupnames, size=(250, -1),
978 action=self.on_groupchoice)
980 wids['grouplist'].SetStringSelection(self.dgroup.groupname)
981 SetTip(wids['grouplist'], 'select a new group, clear undo history')
983 deconv_ops = ('Lorenztian', 'Gaussian')
985 wids['deconv_op'] = Choice(panel, choices=deconv_ops, size=(150, -1),
986 action=self.on_deconvolve)
988 wids['esigma'] = FloatSpin(panel, value=0.5, digits=2, size=(90, -1),
989 increment=0.1, action=self.on_deconvolve)
991 #wids['apply'] = Button(panel, 'Save / Overwrite', size=(150, -1),
992 # action=self.on_apply)
993 #SetTip(wids['apply'], 'Save corrected data, overwrite current arrays')
995 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
996 action=self.on_saveas)
997 SetTip(wids['save_as'], 'Save corrected data as new group')
999 wids['save_as_name'] = wx.TextCtrl(panel, -1, self.dgroup.filename + '_deconv',
1000 size=(250, -1))
1002 def add_text(text, dcol=1, newrow=True):
1003 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
1005 add_text('Deconvolve Data for Group: ', newrow=False)
1006 panel.Add(wids['grouplist'], dcol=5)
1008 add_text('Functional Form: ')
1009 panel.Add(wids['deconv_op'])
1011 add_text(' sigma= ')
1012 panel.Add(wids['esigma'])
1013 # panel.Add(wids['apply'], newrow=True)
1014 panel.Add(wids['save_as'], newrow=True)
1015 panel.Add(wids['save_as_name'], dcol=5)
1016 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
1017 newrow=True)
1018 panel.pack()
1020 fit_dialog_window(self, panel)
1021 self.plot_results(keep_limits=False)
1023 def onDone(self, event=None):
1024 self.Destroy()
1026 def on_saveas(self, event=None):
1027 wids = self.wids
1028 fname = wids['grouplist'].GetStringSelection()
1029 new_fname = wids['save_as_name'].GetValue()
1030 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
1031 xdat, ydat = self.data
1032 ngroup.energy = ngroup.xdat = xdat
1033 ngroup.mu = ngroup.ydat = ydat
1035 ogroup = self.controller.get_group(fname)
1036 olddesc = ogroup.journal.get('source_desc').value
1038 self.parent.install_group(ngroup, journal=ogroup.journal)
1039 ngroup.journal.add('source_desc', f"deconvolved({olddesc})")
1040 ngroup.journal.add('deconvolve_command', self.cmd)
1041 self.parent.process_normalization(ngroup)
1044 def on_groupchoice(self, event=None):
1045 self.dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
1046 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_deconv')
1047 self.plot_results()
1049 def on_deconvolve(self, event=None, value=None):
1050 deconv_form = self.wids['deconv_op'].GetStringSelection()
1052 esigma = self.wids['esigma'].GetValue()
1054 dopts = [self.dgroup.groupname,
1055 "form='%s'" % (deconv_form),
1056 "esigma=%.4f" % (esigma)]
1057 self.cmd = "xas_deconvolve(%s)" % (', '.join(dopts))
1058 self.controller.larch.eval(self.cmd)
1060 self.data = self.dgroup.energy[:], self.dgroup.deconv[:]
1061 self.plot_results()
1063 def on_apply(self, event=None):
1064 xdat, ydat = self.data
1065 dgroup = self.dgroup
1066 dgroup.energy = xdat
1067 dgroup.mu = ydat
1068 dgroup.journal.add('deconvolve_command ', self.cmd)
1069 self.parent.process_normalization(dgroup)
1070 self.plot_results()
1072 def plot_results(self, event=None, keep_limits=True):
1073 ppanel = self.controller.get_display(stacked=False).panel
1074 xnew, ynew = self.data
1075 dgroup = self.dgroup
1076 xlim, ylim = get_view_limits(ppanel)
1077 path, fname = os.path.split(dgroup.filename)
1079 opts = {'delay_draw': True}
1080 if self.controller.plot_erange is not None:
1081 opts['xmin'] = dgroup.e0 + self.controller.plot_erange[0]
1082 opts['xmax'] = dgroup.e0 + self.controller.plot_erange[1]
1084 ppanel.plot(xnew, ynew, zorder=20, marker=None,
1085 linewidth=3, title='Deconvolving:\n %s' % fname,
1086 label='deconvolved', xlabel=plotlabels.energy,
1087 ylabel=plotlabels.mu, **opts)
1089 xold, yold = self.dgroup.energy, self.dgroup.norm
1090 ppanel.oplot(xold, yold, zorder=10,
1091 marker='o', markersize=4, linewidth=2.0,
1092 label='original', show_legend=True, **opts)
1093 if keep_limits:
1094 set_view_limits(ppanel, xlim, ylim)
1095 ppanel.canvas.draw()
1097 def GetResponse(self):
1098 raise AttributeError("use as non-modal dialog!")
1100class DeglitchDialog(wx.Dialog):
1101 """dialog for deglitching or removing unsightly data points"""
1102 def __init__(self, parent, controller, **kws):
1103 self.parent = parent
1104 self.controller = controller
1105 self.wids = {}
1106 self.dgroup = self.controller.get_group()
1107 groupnames = list(self.controller.file_groups.keys())
1109 self.reset_data_history()
1110 xdat, ydat = self.data
1112 xrange = (max(xdat) - min(xdat))
1113 xmax = int(max(xdat) + xrange/5.0)
1114 xmin = int(min(xdat) - xrange/5.0)
1116 lastx, lasty = self.controller.get_cursor()
1117 if lastx is None:
1118 lastx = max(xdat)
1120 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400),
1121 title="Select Points to Remove")
1122 self.SetFont(Font(FONTSIZE))
1123 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
1124 wids = self.wids
1126 wids['grouplist'] = Choice(panel, choices=groupnames, size=(250, -1),
1127 action=self.on_groupchoice)
1129 wids['grouplist'].SetStringSelection(self.dgroup.filename)
1130 SetTip(wids['grouplist'], 'select a new group, clear undo history')
1132 br_xlast = Button(panel, 'Remove point', size=(125, -1),
1133 action=partial(self.on_remove, opt='x'))
1135 br_range = Button(panel, 'Remove range', size=(125, -1),
1136 action=partial(self.on_remove, opt='range'))
1138 undo = Button(panel, 'Undo remove', size=(125, -1),
1139 action=self.on_undo)
1140 #wids['apply'] = Button(panel, 'Save / Overwrite', size=(150, -1),
1141 # action=self.on_apply)
1142 #SetTip(wids['apply'], '''Save deglitched, overwrite current arrays,
1143#clear undo history''')
1145 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
1146 action=self.on_saveas)
1147 SetTip(wids['save_as'], 'Save deglitched data as new group')
1149 wids['save_as_name'] = wx.TextCtrl(panel, -1, self.dgroup.filename + '_clean',
1150 size=(250, -1))
1152 self.history_message = SimpleText(panel, '')
1154 opts = dict(size=(125, -1), digits=2, increment=0.1, action=None)
1155 for wname in ('xlast', 'range1', 'range2'):
1156 if wname == 'range2': lastx += 1
1157 pin_callback = partial(self.on_pinvalue, opt=wname)
1158 fspin, pinbtn = add_floatspin(wname, lastx, panel,
1159 with_pin=True, xasmain=self.parent,
1160 callback=pin_callback, **opts)
1161 wids[wname] = fspin
1162 wids[wname+'_pin'] = pinbtn
1164 self.choice_range = Choice(panel, choices=('above', 'below', 'between'),
1165 size=(90, -1), action=self.on_rangechoice)
1167 self.choice_range.SetStringSelection('above')
1168 wids['range2'].Disable()
1170 wids['plotopts'] = Choice(panel, choices=list(DEGLITCH_PLOTS.keys()),
1171 size=(175, -1),
1172 action=self.on_plotchoice)
1174 wids['plotopts'].SetStringSelection(NORM_MU)
1176 def add_text(text, dcol=1, newrow=True):
1177 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
1179 add_text('Deglitch Data for Group: ', dcol=2, newrow=False)
1180 panel.Add(wids['grouplist'], dcol=5)
1182 add_text('Single Energy : ', dcol=2)
1183 panel.Add(wids['xlast'])
1184 panel.Add(wids['xlast_pin'])
1185 panel.Add(br_xlast)
1187 add_text('Plot Data as: ', dcol=2)
1188 panel.Add(wids['plotopts'], dcol=5)
1190 add_text('Energy Range : ')
1191 panel.Add(self.choice_range)
1192 panel.Add(wids['range1'])
1193 panel.Add(wids['range1_pin'])
1194 panel.Add(br_range)
1196 panel.Add((10, 10), dcol=2, newrow=True)
1197 panel.Add(wids['range2'])
1198 panel.Add(wids['range2_pin'])
1200 # panel.Add(wids['apply'], dcol=2, newrow=True)
1202 panel.Add(wids['save_as'], dcol=2, newrow=True)
1203 panel.Add(wids['save_as_name'], dcol=4)
1204 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
1205 dcol=2, newrow=True)
1206 panel.Add(self.history_message, dcol=2)
1207 panel.Add(undo)
1209 panel.pack()
1211 fit_dialog_window(self, panel)
1212 self.plot_results(keep_limits=False)
1214 def onDone(self, event=None):
1215 self.Destroy()
1217 def reset_data_history(self):
1218 plottype = 'norm'
1219 if 'plotopts' in self.wids:
1220 plotstr = self.wids['plotopts'].GetStringSelection()
1221 plottype = DEGLITCH_PLOTS[plotstr]
1222 self.data = self.get_xydata(datatype=plottype)
1223 self.xmasks = [np.ones(len(self.data[0]), dtype=bool)]
1225 def get_xydata(self, datatype='mu'):
1226 if hasattr(self.dgroup, 'energy'):
1227 xdat = self.dgroup.energy[:]
1228 else:
1229 xdat = self.dgroup.xdat[:]
1230 ydat = self.dgroup.ydat[:]
1231 if datatype == 'mu' and hasattr(self.dgroup, 'mu'):
1232 ydat = self.dgroup.mu[:]
1233 elif datatype == 'norm':
1234 if not hasattr(self.dgroup, 'norm'):
1235 self.parent.process_normalization(dgroup)
1236 ydat = self.dgroup.norm[:]
1237 elif datatype in ('chie', 'chiew'):
1238 if not hasattr(self.dgroup, 'chie'):
1239 self.parent.process_exafs(self.dgroup)
1240 ydat = self.dgroup.chie[:]
1241 if datatype == 'chiew':
1242 ydat = self.dgroup.chie[:] * (xdat-self.dgroup.e0)
1243 return (xdat, ydat)
1245 def on_groupchoice(self, event=None):
1246 self.dgroup = self.controller.get_group(self.wids['grouplist'].GetStringSelection())
1247 self.wids['save_as_name'].SetValue(self.dgroup.filename + '_clean')
1248 self.reset_data_history()
1249 self.plot_results(use_zoom=True)
1251 def on_rangechoice(self, event=None):
1252 sel = self.choice_range.GetStringSelection()
1253 self.wids['range2'].Enable(sel == 'between')
1256 def on_plotchoice(self, event=None):
1257 plotstr = self.wids['plotopts'].GetStringSelection()
1258 plottype = DEGLITCH_PLOTS[plotstr]
1259 self.data = self.get_xydata(datatype=plottype)
1260 self.plot_results()
1262 def on_pinvalue(self, opt='__', xsel=None, **kws):
1263 if xsel is not None and opt in self.wids:
1264 self.wids[opt].SetValue(xsel)
1266 def on_remove(self, event=None, opt=None):
1267 xwork, ywork = self.data
1268 mask = copy.deepcopy(self.xmasks[-1])
1269 if opt == 'x':
1270 bad = index_nearest(xwork, self.wids['xlast'].GetValue())
1271 mask[bad] = False
1272 elif opt == 'range':
1273 rchoice = self.choice_range.GetStringSelection().lower()
1274 x1 = index_nearest(xwork, self.wids['range1'].GetValue())
1275 x2 = None
1276 if rchoice == 'below':
1277 x2, x1 = x1, x2
1278 elif rchoice == 'between':
1279 x2 = index_nearest(xwork, self.wids['range2'].GetValue())
1280 if x1 > x2:
1281 x1, x2 = x2, x1
1282 mask[x1:x2] = False
1283 self.xmasks.append(mask)
1284 self.plot_results()
1286 def on_undo(self, event=None):
1287 if len(self.xmasks) == 1:
1288 self.xmasks = [np.ones(len(self.data[0]), dtype=bool)]
1289 else:
1290 self.xmasks.pop()
1291 self.plot_results()
1293 def on_apply(self, event=None):
1294 xdat, ydat = self.get_xydata(datatype='raw')
1295 mask = self.xmasks[-1]
1296 dgroup = self.dgroup
1297 energies_removed = xdat[np.where(~mask)].tolist()
1298 dgroup.energy = dgroup.xdat = xdat[mask]
1299 dgroup.mu = dgroup.ydat = ydat[mask]
1300 self.reset_data_history()
1301 dgroup.journal.add('deglitch_removed_energies', energies_removed)
1302 self.parent.process_normalization(dgroup)
1303 self.plot_results()
1305 def on_saveas(self, event=None):
1306 fname = self.wids['grouplist'].GetStringSelection()
1307 new_fname = self.wids['save_as_name'].GetValue()
1308 ngroup = self.controller.copy_group(fname, new_filename=new_fname)
1309 xdat, ydat = self.get_xydata(datatype='mu')
1310 mask = self.xmasks[-1]
1311 energies_removed = xdat[np.where(~mask)].tolist()
1313 ngroup.energy = ngroup.xdat = xdat[mask]
1314 ngroup.mu = ngroup.ydat = ydat[mask]
1315 ngroup.energy_orig = 1.0*ngroup.energy
1317 ogroup = self.controller.get_group(fname)
1318 olddesc = ogroup.journal.get('source_desc').value
1320 self.parent.install_group(ngroup, journal=ogroup.journal)
1321 ngroup.journal.add('source_desc', f"deglitched({olddesc})")
1322 ngroup.journal.add('deglitch_removed_energies', energies_removed)
1324 self.parent.process_normalization(ngroup)
1326 def plot_results(self, event=None, keep_limits=True):
1327 ppanel = self.controller.get_display(stacked=False).panel
1329 xdat, ydat = self.data
1331 xmin = min(xdat) - 0.025*(max(xdat) - min(xdat))
1332 xmax = max(xdat) + 0.025*(max(xdat) - min(xdat))
1333 ymin = min(ydat) - 0.025*(max(ydat) - min(ydat))
1334 ymax = max(ydat) + 0.025*(max(ydat) - min(ydat))
1336 dgroup = self.dgroup
1338 path, fname = os.path.split(dgroup.filename)
1340 plotstr = self.wids['plotopts'].GetStringSelection()
1341 plottype = DEGLITCH_PLOTS[plotstr]
1343 xlabel=plotlabels.energy
1344 if plottype in ('chie', 'chiew'):
1345 xmin = self.dgroup.e0
1346 xlabel = xlabel=plotlabels.ewithk
1348 opts = dict(xlabel=xlabel, title='De-glitching:\n %s' % fname,
1349 delay_draw=True)
1351 ylabel = {'mu': plotlabels.mu,
1352 'norm': plotlabels.norm,
1353 'chie': plotlabels.chie,
1354 'chiew': plotlabels.chiew.format(1),
1355 }.get(plottype, plotlabels.norm)
1357 dgroup.plot_xlabel = xlabel
1358 dgroup.plot_ylabel = ylabel
1360 xlim, ylim = get_view_limits(ppanel)
1362 ppanel.plot(xdat, ydat, zorder=10, marker=None, linewidth=3,
1363 label='original', ylabel=ylabel, **opts)
1365 if len(self.xmasks) > 1:
1366 mask = self.xmasks[-1]
1367 ppanel.oplot(xdat[mask], ydat[mask], zorder=15,
1368 marker='o', markersize=3, linewidth=2.0,
1369 label='current', show_legend=True, **opts)
1371 def ek_formatter(x, pos):
1372 ex = float(x) - self.dgroup.e0
1373 s = '' if ex < 0 else '\n[%.1f]' % (etok(ex))
1374 return r"%1.4g%s" % (x, s)
1376 if keep_limits:
1377 set_view_limits(ppanel, xlim, ylim)
1378 if plottype in ('chie', 'chiew'):
1379 ppanel.axes.xaxis.set_major_formatter(FuncFormatter(ek_formatter))
1381 ppanel.canvas.draw()
1383 self.history_message.SetLabel('%i items in history' % (len(self.xmasks)-1))
1385 def GetResponse(self):
1386 raise AttributeError("use as non-modal dialog!")
1389SPECCALC_SETUP = """#From SpectraCalc dialog:
1390_x = {group:s}.{xname:s}
1391a = {group:s}.{yname:s}
1392b = c = d = e = f = g = None
1393"""
1395SPECCALC_INTERP = "{key:s} = interp({group:s}.{xname:s}, {group:s}.{yname:s}, _x)"
1396SPECCALC_PLOT = """plot(_x, ({expr:s}), label='{expr:s}', new=True,
1397 show_legend=True, xlabel='{xname:s}', title='Spectral Calculation')"""
1399SPECCALC_SAVE = """{new:s} = copy_xafs_group({group:s})
1400{new:s}.groupname = '{new:s}'
1401{new:s}.mu = ({expr:s})
1402{new:s}.filename = '{fname:s}'
1403{new:s}.journal.add('calc_groups', {group_map:s})
1404{new:s}.journal.add('calc_arrayname', '{yname:s}')
1405{new:s}.journal.add('calc_expression', '{expr:s}')
1406del _x, a, b, c, d, e, f, g"""
1409class SpectraCalcDialog(wx.Dialog):
1410 """dialog for adding and subtracting spectra"""
1411 def __init__(self, parent, controller, **kws):
1413 self.parent = parent
1414 self.controller = controller
1415 self.dgroup = self.controller.get_group()
1416 self.group_a = None
1417 groupnames = list(self.controller.file_groups.keys())
1419 self.data = [self.dgroup.energy[:], self.dgroup.norm[:]]
1420 xmin = min(self.dgroup.energy)
1421 xmax = max(self.dgroup.energy)
1422 e0val = getattr(self.dgroup, 'e0', xmin)
1424 wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(475, 525),
1425 title="Spectra Calculations: Add, Subtract Spectra")
1426 self.SetFont(Font(FONTSIZE))
1427 panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT)
1429 def add_text(text, dcol=1, newrow=True):
1430 panel.Add(SimpleText(panel, text), dcol=dcol, newrow=newrow)
1432 self.wids = wids = {}
1433 array_choices = ('Normalized \u03BC(E)', 'Raw \u03BC(E)')
1435 wids['array'] = Choice(panel, choices=array_choices, size=(250, -1))
1437 add_text('Array to use: ', newrow=True)
1438 panel.Add(wids['array'], dcol=2)
1440 # group 'a' cannot be none, and defaults to current group
1441 gname = 'a'
1442 wname = 'group_%s' % gname
1443 wids[wname] = Choice(panel, choices=groupnames, size=(250, -1))
1444 wids[wname].SetStringSelection(self.dgroup.filename)
1445 add_text(' %s = ' % gname, newrow=True)
1446 panel.Add(wids[wname], dcol=2)
1448 groupnames.insert(0, 'None')
1449 for gname in ('b', 'c', 'd', 'e', 'f', 'g'):
1450 wname = 'group_%s' % gname
1451 wids[wname] = Choice(panel, choices=groupnames, size=(250, -1))
1452 wids[wname].SetSelection(0)
1453 add_text(' %s = ' % gname, newrow=True)
1454 panel.Add(wids[wname], dcol=2)
1456 wids['formula'] = wx.TextCtrl(panel, -1, 'a-b', size=(250, -1))
1457 add_text('Expression = ', newrow=True)
1458 panel.Add(wids['formula'], dcol=2)
1460 wids['docalc'] = Button(panel, 'Calculate',
1461 size=(150, -1), action=self.on_docalc)
1463 panel.Add(wids['docalc'], dcol=2, newrow=True)
1465 wids['save_as'] = Button(panel, 'Save As New Group: ', size=(150, -1),
1466 action=self.on_saveas)
1467 SetTip(wids['save_as'], 'Save as new group')
1469 wids['save_as_name'] = wx.TextCtrl(panel, -1,
1470 self.dgroup.filename + '_calc',
1471 size=(250, -1))
1472 panel.Add(wids['save_as'], newrow=True)
1473 panel.Add(wids['save_as_name'], dcol=2)
1474 wids['save_as'].Disable()
1475 panel.Add(Button(panel, 'Done', size=(150, -1), action=self.onDone),
1476 newrow=True)
1477 panel.pack()
1478 fit_dialog_window(self, panel)
1480 def onDone(self, event=None):
1481 self.Destroy()
1483 def on_docalc(self, event=None):
1484 self.expr = self.wids['formula'].GetValue()
1486 self.yname = 'mu'
1487 if self.wids['array'].GetStringSelection().lower().startswith('norm'):
1488 self.yname = 'norm'
1490 groups = {}
1491 for aname in ('a', 'b', 'c', 'd', 'e', 'f', 'g'):
1492 fname = self.wids['group_%s' % aname].GetStringSelection()
1493 if fname not in (None, 'None'):
1494 grp = self.controller.get_group(fname)
1495 groups[aname] = grp
1497 self.group_map = {key: group.groupname for key, group in groups.items()}
1498 # note: 'a' cannot be None, all others can be None
1499 group_a = self.group_a = groups.pop('a')
1500 xname = 'energy'
1501 if not hasattr(group_a, xname):
1502 xname = 'xdat'
1504 cmds = [SPECCALC_SETUP.format(group=group_a.groupname,
1505 xname=xname, yname=self.yname)]
1507 for key, group in groups.items():
1508 cmds.append(SPECCALC_INTERP.format(key=key, group=group.groupname,
1509 xname=xname, yname=self.yname))
1511 cmds.append(SPECCALC_PLOT.format(expr=self.expr, xname=xname))
1512 self.controller.larch.eval('\n'.join(cmds))
1513 self.wids['save_as'].Enable()
1515 def on_saveas(self, event=None):
1516 wids = self.wids
1517 _larch = self.controller.larch
1518 fname = wids['group_a'].GetStringSelection()
1519 new_fname =self.wids['save_as_name'].GetValue()
1520 new_gname = file2groupname(new_fname, slen=5, symtable=_larch.symtable)
1522 gmap = []
1523 for k, v in self.group_map.items():
1524 gmap.append(f'"{k}": "{v}"')
1525 gmap = '{%s}' % (', '.join(gmap))
1527 _larch.eval(SPECCALC_SAVE.format(new=new_gname, fname=new_fname,
1528 group=self.group_a.groupname,
1529 group_map=gmap,
1530 yname=self.yname, expr=self.expr))
1533 journal={'source_desc': f"{new_fname}: calc({self.expr})",
1534 'calc_groups': gmap, 'calc_expression': self.expr}
1536 ngroup = getattr(_larch.symtable, new_gname, None)
1537 if ngroup is not None:
1538 self.parent.install_group(ngroup, source=journal['source_desc'],
1539 journal=journal)
1541 def GetResponse(self):
1542 raise AttributeError("use as non-modal dialog!")
1544class EnergyUnitsDialog(wx.Dialog):
1545 """dialog for selecting, changing energy units, forcing data to eV"""
1546 unit_choices = ['eV', 'keV', 'deg', 'steps']
1548 def __init__(self, parent, energy_array, unitname='eV',dspace=1, **kws):
1550 self.parent = parent
1551 self.energy = 1.0*energy_array
1553 title = "Select Energy Units to convert to 'eV'"
1554 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
1555 self.SetFont(Font(FONTSIZE))
1556 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1558 self.en_units = Choice(panel, choices=self.unit_choices, size=(125, -1),
1559 action=self.onUnits)
1560 self.en_units.SetStringSelection(unitname)
1561 self.mono_dspace = FloatCtrl(panel, value=dspace, minval=0, maxval=100.0,
1562 precision=6, size=(125, -1))
1563 self.steps2deg = FloatCtrl(panel, value=1.0, minval=0,
1564 precision=1, size=(125, -1))
1566 self.mono_dspace.Disable()
1567 self.steps2deg.Disable()
1569 panel.Add(SimpleText(panel, 'Energy Units : '), newrow=True)
1570 panel.Add(self.en_units)
1572 panel.Add(SimpleText(panel, 'Mono D spacing : '), newrow=True)
1573 panel.Add(self.mono_dspace)
1575 panel.Add(SimpleText(panel, 'Mono Steps per Degree : '), newrow=True)
1576 panel.Add(self.steps2deg)
1577 panel.Add((5, 5))
1579 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1580 panel.pack()
1582 fit_dialog_window(self, panel)
1585 def onUnits(self, event=None):
1586 units = self.en_units.GetStringSelection()
1587 self.steps2deg.Enable(units == 'steps')
1588 self.mono_dspace.Enable(units in ('steps', 'deg'))
1590 def GetResponse(self, master=None, gname=None, ynorm=True):
1591 self.Raise()
1592 response = namedtuple('EnergyUnitsResponse',
1593 ('ok', 'units', 'energy', 'dspace'))
1594 ok, units, en, dspace = False, 'eV', None, -1
1596 if self.ShowModal() == wx.ID_OK:
1597 units = self.en_units.GetStringSelection()
1598 if units == 'eV':
1599 en = self.energy
1600 elif units == 'keV':
1601 en = self.energy * 1000.0
1602 elif units in ('steps', 'deg'):
1603 dspace = float(self.mono_dspace.GetValue())
1604 if units == 'steps':
1605 self.energy /= self.steps2deg.GetValue()
1606 en = PLANCK_HC/(2*dspace*np.sin(self.energy * DEG2RAD))
1607 ok = True
1608 return response(ok, units, en, dspace)
1610class MergeDialog(wx.Dialog):
1611 """dialog for merging groups"""
1612 ychoices = ['raw mu(E)', 'normalized mu(E)']
1614 def __init__(self, parent, groupnames, outgroup='merge', **kws):
1615 title = "Merge %i Selected Groups" % (len(groupnames))
1616 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
1618 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1620 self.master_group = Choice(panel, choices=groupnames, size=(250, -1))
1621 self.yarray_name = Choice(panel, choices=self.ychoices, size=(250, -1))
1622 self.group_name = wx.TextCtrl(panel, -1, outgroup, size=(250, -1))
1624 panel.Add(SimpleText(panel, 'Match Energy to : '), newrow=True)
1625 panel.Add(self.master_group)
1627 panel.Add(SimpleText(panel, 'Array to merge : '), newrow=True)
1628 panel.Add(self.yarray_name)
1630 panel.Add(SimpleText(panel, 'New group name : '), newrow=True)
1631 panel.Add(self.group_name)
1633 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1635 panel.pack()
1636 fit_dialog_window(self, panel)
1639 def GetResponse(self, master=None, gname=None, ynorm=True):
1640 self.Raise()
1641 response = namedtuple('MergeResponse', ('ok', 'master', 'ynorm', 'group'))
1642 ok = False
1643 if self.ShowModal() == wx.ID_OK:
1644 master= self.master_group.GetStringSelection()
1645 ynorm = 'norm' in self.yarray_name.GetStringSelection().lower()
1646 gname = self.group_name.GetValue()
1647 ok = True
1648 return response(ok, master, ynorm, gname)
1651class ExportCSVDialog(wx.Dialog):
1652 """dialog for exporting groups to CSV file"""
1654 def __init__(self, parent, groupnames, **kws):
1655 title = "Export Selected Groups"
1656 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
1657 self.SetFont(Font(FONTSIZE))
1658 self.xchoices = {'Energy': 'energy',
1659 'k': 'k',
1660 'R': 'r',
1661 'q': 'q'}
1663 self.ychoices = {'normalized mu(E)': 'norm',
1664 'raw mu(E)': 'mu',
1665 'flattened mu(E)': 'flat',
1666 'd mu(E) / dE': 'dmude',
1667 'chi(k)': 'chi',
1668 'chi(E)': 'chie',
1669 'chi(q)': 'chiq',
1670 '|chi(R)|': 'chir_mag',
1671 'Re[chi(R)]': 'chir_re'}
1673 self.delchoices = {'comma': ',',
1674 'space': ' ',
1675 'tab': '\t'}
1678 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1680 self.save_individual_files = Check(panel, default=False, label='Save individual files', action=self.onSaveIndividualFiles)
1681 self.master_group = Choice(panel, choices=groupnames, size=(200, -1))
1682 self.xarray_name = Choice(panel, choices=list(self.xchoices.keys()),size=(200, -1))
1683 self.yarray_name = Choice(panel, choices=list(self.ychoices.keys()), size=(200, -1))
1684 self.del_name = Choice(panel, choices=list(self.delchoices.keys()), size=(200, -1))
1687 panel.Add(self.save_individual_files, newrow=True)
1689 panel.Add(SimpleText(panel, 'Group for Energy Array: '), newrow=True)
1690 panel.Add(self.master_group)
1692 panel.Add(SimpleText(panel, 'X Array to Export: '), newrow=True)
1693 panel.Add(self.xarray_name)
1695 panel.Add(SimpleText(panel, 'Y Array to Export: '), newrow=True)
1696 panel.Add(self.yarray_name)
1698 panel.Add(SimpleText(panel, 'Delimeter for File: '), newrow=True)
1699 panel.Add(self.del_name)
1700 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1701 panel.pack()
1702 fit_dialog_window(self, panel)
1704 def onSaveIndividualFiles(self, event=None):
1705 save_individual = self.save_individual_files.IsChecked()
1706 self.master_group.Enable(not save_individual)
1708 def GetResponse(self, master=None, gname=None, ynorm=True):
1709 self.Raise()
1710 response = namedtuple('ExportCSVResponse',
1711 ('ok', 'individual', 'master', 'xarray', 'yarray', 'delim'))
1712 ok = False
1713 if self.ShowModal() == wx.ID_OK:
1714 individual = self.save_individual_files.IsChecked()
1715 master = self.master_group.GetStringSelection()
1716 xarray = self.xchoices[self.xarray_name.GetStringSelection()]
1717 yarray = self.ychoices[self.yarray_name.GetStringSelection()]
1718 delim = self.delchoices[self.del_name.GetStringSelection()]
1719 ok = True
1720 return response(ok, individual, master, xarray, yarray, delim)
1722class QuitDialog(wx.Dialog):
1723 """dialog for quitting, prompting to save project"""
1725 def __init__(self, parent, message, **kws):
1726 title = "Quit Larch XAS Viewer?"
1727 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title, size=(500, 150))
1728 self.SetFont(Font(FONTSIZE))
1729 self.needs_save = True
1730 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1732 status, filename, stime = message
1733 warn_msg = 'All work in this session will be lost!'
1735 panel.Add((5, 5))
1736 if len(stime) > 2:
1737 status = f"{status} at {stime} to file"
1738 warn_msg = 'Changes made after that will be lost!'
1740 panel.Add(wx.StaticText(panel, label=status), dcol=2)
1742 if len(filename) > 0:
1743 if filename.startswith("'") and filename.endswith("'"):
1744 filename = filename[1:-1]
1745 panel.Add((15, 5), newrow=True)
1746 panel.Add(wx.StaticText(panel, label=filename), dcol=2)
1748 panel.Add((5, 5), newrow=True)
1749 panel.Add(wx.StaticText(panel, label=warn_msg), dcol=2)
1750 panel.Add(HLine(panel, size=(500, 3)), dcol=3, newrow=True)
1751 panel.Add((5, 5), newrow=True)
1752 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1753 panel.pack()
1755 fit_dialog_window(self, panel)
1757 def GetResponse(self):
1758 self.Raise()
1759 response = namedtuple('QuitResponse', ('ok',))
1760 ok = (self.ShowModal() == wx.ID_OK)
1761 return response(ok,)
1763class RenameDialog(wx.Dialog):
1764 """dialog for renaming group"""
1765 def __init__(self, parent, oldname, **kws):
1766 title = "Rename Group %s" % (oldname)
1767 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
1768 self.SetFont(Font(FONTSIZE))
1769 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1771 self.newname = wx.TextCtrl(panel, -1, oldname, size=(250, -1))
1773 panel.Add(SimpleText(panel, 'Old Name : '), newrow=True)
1774 panel.Add(SimpleText(panel, oldname))
1775 panel.Add(SimpleText(panel, 'New Name : '), newrow=True)
1776 panel.Add(self.newname)
1777 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1779 panel.pack()
1780 fit_dialog_window(self, panel)
1783 def GetResponse(self, newname=None):
1784 self.Raise()
1785 response = namedtuple('RenameResponse', ('ok', 'newname'))
1786 ok = False
1787 if self.ShowModal() == wx.ID_OK:
1788 newname = self.newname.GetValue()
1789 ok = True
1790 return response(ok, newname)
1792class RemoveDialog(wx.Dialog):
1793 """dialog for removing groups"""
1794 def __init__(self, parent, grouplist, **kws):
1795 title = "Remove %i Selected Group" % len(grouplist)
1796 self.grouplist = grouplist
1797 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
1798 self.SetFont(Font(FONTSIZE))
1799 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1801 panel.Add(SimpleText(panel, 'Remove %i Selected Groups?' % (len(grouplist))),
1802 newrow=True, dcol=2)
1804 panel.Add(OkCancel(panel), dcol=2, newrow=True)
1805 panel.pack()
1806 fit_dialog_window(self, panel)
1808 def GetResponse(self, ngroups=None):
1809 self.Raise()
1810 response = namedtuple('RemoveResponse', ('ok','ngroups'))
1811 ok = False
1812 if self.ShowModal() == wx.ID_OK:
1813 ngroups = len(self.grouplist)
1814 ok = True
1815 return response(ok, ngroups)
1818class LoadSessionDialog(wx.Frame):
1819 """Read, show data from saved larch session"""
1821 xasgroups_name = '_xasgroups'
1822 feffgroups_name = ['_feffpaths', '_feffcache']
1824 def __init__(self, parent, session, filename, controller, **kws):
1825 self.parent = parent
1826 self.session = session
1827 self.filename = filename
1828 self.controller = controller
1829 title = f"Read Larch Session from '{filename}'"
1830 wx.Frame.__init__(self, parent, wx.ID_ANY, title=title)
1832 x0, y0 = parent.GetPosition()
1833 self.SetPosition((x0+450, y0+75))
1835 splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
1836 splitter.SetMinimumPaneSize(250)
1838 leftpanel = wx.Panel(splitter)
1839 rightpanel = wx.Panel(splitter)
1841 ltop = wx.Panel(leftpanel)
1843 sel_none = Button(ltop, 'Select None', size=(100, 30), action=self.onSelNone)
1844 sel_all = Button(ltop, 'Select All', size=(100, 30), action=self.onSelAll)
1845 sel_imp = Button(ltop, 'Import Selected Data', size=(200, 30),
1846 action=self.onImport)
1848 self.select_imported = sel_imp
1849 self.grouplist = FileCheckList(leftpanel, select_action=self.onShowGroup)
1850 set_color(self.grouplist, 'list_fg', bg='list_bg')
1852 tsizer = wx.GridBagSizer(2, 2)
1853 tsizer.Add(sel_all, (0, 0), (1, 1), LEFT, 0)
1854 tsizer.Add(sel_none, (0, 1), (1, 1), LEFT, 0)
1855 tsizer.Add(sel_imp, (1, 0), (1, 2), LEFT, 0)
1857 pack(ltop, tsizer)
1859 sizer = wx.BoxSizer(wx.VERTICAL)
1860 sizer.Add(ltop, 0, LEFT|wx.GROW, 1)
1861 sizer.Add(self.grouplist, 1, LEFT|wx.GROW|wx.ALL, 1)
1862 pack(leftpanel, sizer)
1865 panel = GridPanel(rightpanel, ncols=3, nrows=4, pad=2, itemstyle=LEFT)
1866 self.wids = wids = {}
1868 top_message = 'Larch Session File: No XAFS Groups'
1869 symtable = controller.symtable
1871 self.allgroups = session.symbols.get(self.xasgroups_name, {})
1872 self.extra_groups = []
1873 for key, val in session.symbols.items():
1874 if key == self.xasgroups_name or key in self.feffgroups_name:
1875 continue
1876 if key in self.allgroups:
1877 continue
1878 if hasattr(val, 'energy') and hasattr(val, 'mu'):
1879 if key in self.allgroups.keys() or key in self.allgroups.values():
1880 continue
1881 self.allgroups[key] = key
1882 self.extra_groups.append(key)
1885 checked = []
1886 for fname, gname in self.allgroups.items():
1887 self.grouplist.Append(fname)
1888 checked.append(fname)
1890 self.grouplist.SetCheckedStrings(checked)
1892 group_names = list(self.allgroups.values())
1893 group_names.append(self.xasgroups_name)
1894 group_names.extend(self.feffgroups_name)
1896 wids['view_conf'] = Button(panel, 'Show Session Configuration',
1897 size=(200, 30), action=self.onShowConfig)
1898 wids['view_cmds'] = Button(panel, 'Show Session Commands',
1899 size=(200, 30), action=self.onShowCommands)
1901 wids['plotopt'] = Choice(panel, choices=list(SESSION_PLOTS.keys()),
1902 action=self.onPlotChoice, size=(175, -1))
1904 panel.Add(wids['view_conf'], dcol=1)
1905 panel.Add(wids['view_cmds'], dcol=1, newrow=False)
1906 panel.Add(HLine(panel, size=(450, 2)), dcol=3, newrow=True)
1908 over_msg = 'Importing these Groups/Data will overwrite values in the current session:'
1909 panel.Add(SimpleText(panel, over_msg), dcol=2, newrow=True)
1910 panel.Add(SimpleText(panel, "Symbol Name"), dcol=1, newrow=True)
1911 panel.Add(SimpleText(panel, "Import/Overwrite?"), dcol=1)
1912 i = 0
1913 self.overwrite_checkboxes = {}
1914 for g in self.session.symbols:
1915 if g not in group_names and hasattr(symtable, g):
1916 chbox = Check(panel, default=True)
1917 panel.Add(SimpleText(panel, g), dcol=1, newrow=True)
1918 panel.Add(chbox, dcol=1)
1919 self.overwrite_checkboxes[g] = chbox
1921 i += 1
1923 panel.Add((5, 5), newrow=True)
1924 panel.Add(HLine(panel, size=(450, 2)), dcol=3, newrow=True)
1925 panel.Add(SimpleText(panel, 'Plot Type:'), newrow=True)
1926 panel.Add(wids['plotopt'], dcol=2, newrow=False)
1927 panel.pack()
1929 self.plotpanel = PlotPanel(rightpanel, messenger=self.plot_messages)
1930 self.plotpanel.SetSize((475, 450))
1931 plotconf = self.controller.get_config('plot')
1932 self.plotpanel.conf.set_theme(plotconf['theme'])
1933 self.plotpanel.conf.enable_grid(plotconf['show_grid'])
1935 sizer = wx.BoxSizer(wx.VERTICAL)
1936 sizer.Add(panel, 0, LEFT, 2)
1937 sizer.Add(self.plotpanel, 1, LEFT, 2)
1939 pack(rightpanel, sizer)
1941 splitter.SplitVertically(leftpanel, rightpanel, 1)
1942 self.SetSize((750, 725))
1944 self.Show()
1945 self.Raise()
1947 def plot_messages(self, msg, panel=1):
1948 pass
1950 def onSelAll(self, event=None):
1951 self.grouplist.SetCheckedStrings(list(self.allgroups.keys()))
1953 def onSelNone(self, event=None):
1954 self.grouplist.SetCheckedStrings([])
1956 def onShowGroup(self, event=None):
1957 """column selections changed calc xdat and ydat"""
1958 fname = event.GetString()
1959 gname = self.allgroups.get(fname, None)
1960 if gname in self.session.symbols:
1961 self.plot_group(gname, fname)
1963 def onPlotChoice(self, event=None):
1964 fname = self.grouplist.GetStringSelection()
1965 gname = self.allgroups.get(fname, None)
1966 self.plot_group(gname, fname)
1968 def plot_group(self, gname, fname):
1969 grp = self.session.symbols[gname]
1970 plottype = SESSION_PLOTS.get(self.wids['plotopt'].GetStringSelection(), 'norm')
1971 xdef = np.zeros(1)
1972 xdat = getattr(grp, 'energy', xdef)
1973 ydat = getattr(grp, 'mu', xdef)
1974 xlabel = plotlabels.energy
1975 ylabel = plotlabels.mu
1976 if plottype == 'norm' and hasattr(grp, 'norm'):
1977 ydat = getattr(grp, 'norm', xdef)
1978 ylabel = plotlabels.norm
1979 elif plottype == 'chikw' and hasattr(grp, 'chi'):
1980 xdat = getattr(grp, 'k', xdef)
1981 ydat = getattr(grp, 'chi', xdef)
1982 ydat = ydat*xdat*xdat
1983 xlabel = plotlabels.chikw.format(2)
1985 if len(ydat) > 1:
1986 self.plotpanel.plot(xdat, ydat, xlabel=xlabel,
1987 ylabel=ylabel, title=fname)
1990 def onShowConfig(self, event=None):
1991 DictFrame(parent=self.parent,
1992 data=self.session.config,
1993 title=f"Session Configuration for '{self.filename}'")
1995 def onShowCommands(self, event=None):
1996 oname = self.filename.replace('.larix', '.lar')
1997 wildcard='Larch Command Files (*.lar)|*.lar'
1998 text = '\n'.join(self.session.command_history)
1999 ReportFrame(parent=self.parent,
2000 text=text,
2001 title=f"Session Commands from '{self.filename}'",
2002 default_filename=oname,
2003 wildcard=wildcard)
2005 def onClose(self, event=None):
2006 self.Destroy()
2008 def onImport(self, event=None):
2009 ignore = []
2010 for gname, chbox in self.overwrite_checkboxes.items():
2011 if not chbox.IsChecked():
2012 ignore.append(gname)
2014 sel_groups = self.grouplist.GetCheckedStrings()
2015 for fname, gname in self.allgroups.items():
2016 if fname not in sel_groups:
2017 ignore.append(gname)
2019 fname = self.filename.replace('\\','/')
2020 if fname.endswith('/'):
2021 fname = fname[:-1]
2022 lcmd = [f"load_session('{fname}'"]
2023 if len(ignore) > 0:
2024 ignore = repr(ignore)
2025 lcmd.append(f", ignore_groups={ignore}")
2026 if len(self.extra_groups) > 0:
2027 extra = repr(self.extra_groups)
2028 lcmd.append(f", include_xasgroups={extra}")
2030 lcmd = ''.join(lcmd) + ')'
2032 cmds = ["# Loading Larch Session with ", lcmd, '######']
2034 self.controller.larch.eval('\n'.join(cmds))
2035 last_fname = None
2036 xasgroups = getattr(self.controller.symtable, self.xasgroups_name, {})
2037 for key, val in xasgroups.items():
2038 if key not in self.controller.filelist.GetItems():
2039 self.controller.filelist.Append(key)
2040 last_fname = key
2042 self.controller.recentfiles.append((time.time(), self.filename))
2044 wx.CallAfter(self.Destroy)
2045 if last_fname is not None:
2046 self.parent.ShowFile(filename=last_fname)