Coverage for larch/wxlib/xrfdisplay.py: 10%
1051 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
1#!/usr/bin/env python
2"""
3GUI Frame for XRF display, reading larch MCA group
5"""
6import sys
7import time
8import copy
9from functools import partial
10from pathlib import Path
11import wx
12import wx.lib.mixins.inspection
13import wx.lib.scrolledpanel as scrolled
14import wx.dataview as dv
15import wx.lib.colourselect as csel
17import numpy as np
18import matplotlib
19from matplotlib.ticker import LogFormatter, FuncFormatter
21from wxmplot import PlotPanel
22from . import (SimpleText, EditableListBox, Font, pack, Popup,
23 get_icon, SetTip, Button, Check, MenuItem, Choice,
24 FileOpen, FileSave, fix_filename, HLine, GridPanel,
25 CEN, LEFT, RIGHT, PeriodicTablePanel,
26 FONTSIZE, FONTSIZE_FW)
28from ..math import index_of
29from ..utils import bytes2str, get_cwd
30from ..io import GSEMCA_File
31from ..site_config import icondir
32from ..interpreter import Interpreter
34from .gui_utils import LarchWxApp
35from .larchframe import LarchFrame
36# from .periodictable import PeriodicTablePanel
38from .xrfdisplay_utils import (XRFCalibrationFrame, ColorsFrame,
39 XrayLinesFrame, XRFDisplayConfig, XRFGROUP,
40 MAKE_XRFGROUP_CMD, next_mcaname)
42from .xrfdisplay_fitpeaks import FitSpectraFrame
44FILE_WILDCARDS = "MCA File (*.mca)|*.mca|All files (*.*)|*.*"
45FILE_ALREADY_READ = """The File
46 '%s'
47has already been read.
48"""
50ICON_FILE = 'ptable.ico'
52read_mcafile = "# {group:s}.{name:s} = read_gsemca('{filename:s}')"
54def txt(panel, label, size=75, colour=None, font=None, style=None):
55 if style is None:
56 style = wx.ALIGN_LEFT|wx.ALL|wx.GROW
57 if colour is None:
58 colour = wx.Colour(0, 0, 50)
59 this = SimpleText(panel, label, size=(size, -1),
60 colour=colour, style=style)
61 if font is not None: this.SetFont(font)
62 return this
64def lin(panel, len=30, wid=2, style=wx.LI_HORIZONTAL):
65 return wx.StaticLine(panel, size=(len, wid), style=style)
68class XRFDisplayFrame(wx.Frame):
69 _about = """XRF Spectral Viewer
70 Matt Newville <newville @ cars.uchicago.edu>
71 """
72 main_title = 'XRF Display'
73 def __init__(self, _larch=None, parent=None, filename=None,
74 size=(725, 450), axissize=None, axisbg=None,
75 title='XRF Display', exit_callback=None,
76 output_title='XRF', roi_callback=None, **kws):
78 if size is None: size = (725, 450)
79 wx.Frame.__init__(self, parent=parent,
80 title=title, size=size, **kws)
81 self.conf = XRFDisplayConfig()
82 self.subframes = {}
83 self.data = None
84 self.title = title
85 self.roi_callback = roi_callback
86 self.plotframe = None
87 self.wids = {}
88 self.larch = _larch
89 if isinstance(self.larch, Interpreter): # called from shell
90 self.larch_buffer = None
91 else:
92 self.larch_buffer = parent
93 if not isinstance(parent, LarchFrame):
94 self.larch_buffer = LarchFrame(_larch=self.larch,
95 is_standalone=False, with_raise=False)
96 self.subframes['larchframe'] = self.larch_buffer
97 self.larch = self.larch_buffer.larchshell
98 self.init_larch()
100 self.exit_callback = exit_callback
101 self.roi_patch = None
102 self.selected_roi = None
103 self.roilist_sel = None
104 self.selected_elem = None
105 self.mca = None
106 self.mca2 = None
107 self.xdata = np.arange(2048)*0.01
108 self.ydata = np.ones(2048)*1.e-4
109 self.x2data = None
110 self.y2data = None
111 self.rois_shown = False
112 self.mca_index = 0
113 self.major_markers = []
114 self.minor_markers = []
115 self.hold_markers = []
117 self.hold_lines = None
118 self.saved_lines = None
119 self.energy_for_zoom = None
120 self.xview_range = None
121 self.show_yaxis = False
122 self.xmarker_left = None
123 self.xmarker_right = None
125 self.highlight_xrayline = None
126 self.highlight_xrayline = None
127 self.cursor_markers = [None, None]
128 self.ylog_scale = True
129 self.SetTitle("%s: %s " % (self.main_title, title))
131 self._menus = []
132 self.createMainPanel()
133 self.createMenus()
134 self.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL))
135 self.statusbar = self.CreateStatusBar(4)
136 self.statusbar.SetStatusWidths([-5, -3, -3, -4])
137 statusbar_fields = ["XRF Display", " ", " ", " "]
138 for i in range(len(statusbar_fields)):
139 self.statusbar.SetStatusText(statusbar_fields[i], i)
140 if filename is not None:
141 if isinstance(filename, Path):
142 filename = Path(filename).absolute().as_posix()
144 self.add_mca(GSEMCA_File(filename), filename=filename, plot=True)
147 def ignoreEvent(self, event=None):
148 pass
150 def on_cursor(self, event=None, side='left'):
151 if event is None:
152 return
153 x, y = event.xdata, event.ydata
154 if len(self.panel.fig.axes) > 1:
155 try:
156 x, y = self.panel.axes.transData.inverted().transform((event.x, event.y))
157 except:
158 pass
159 ix = x
160 if self.mca is not None:
161 try:
162 ix = index_of(self.mca.energy, x)
163 except TypeError:
164 pass
166 if side == 'right':
167 self.xmarker_right = ix
168 elif side == 'left':
169 self.xmarker_left = ix
171 if self.xmarker_left is not None and self.xmarker_right is not None:
172 ix1, ix2 = self.xmarker_left, self.xmarker_right
173 self.xmarker_left = min(ix1, ix2)
174 self.xmarker_right = max(ix1, ix2)
176 if side == 'left' and ix is not None:
177 self.energy_for_zoom = self.mca.energy[ix]
178 self.update_status()
179 self.draw()
181 def clear_lines(self, evt=None):
182 "remove all Line Markers"
183 for m in self.major_markers + self.minor_markers + self.hold_markers:
184 try:
185 m.remove()
186 except:
187 pass
188 if self.highlight_xrayline is not None:
189 try:
190 self.highlight_xrayline.remove()
191 except:
192 pass
194 self.highlight_xrayline = None
195 self.major_markers = []
196 self.minor_markers = []
197 self.hold_markers = []
198 self.draw()
200 def draw(self):
201 try:
202 self.panel.canvas.draw()
203 except:
204 pass
206 def clear_markers(self, evt=None):
207 "remove all Cursor Markers"
208 for m in self.cursor_markers:
209 if m is not None:
210 m.remove()
211 self.cursor_markers = [None, None]
212 self.xmarker_left = None
213 self.xmarker_right = None
214 self.draw()
216 def clear_background(self, evt=None):
217 "remove XRF background"
218 self.mca2 = None
219 self.plotmca(self.mca)
221 def update_status(self):
222 fmt = "{:s}:{:}, E={:.3f}, Cts={:,.0f}".format
223 if (self.xmarker_left is None and
224 self.xmarker_right is None and
225 self.selected_roi is None):
226 return
228 log = np.log10
229 axes= self.panel.axes
230 def draw_ymarker_range(idx, x, y):
231 ymin, ymax = self.panel.axes.get_ylim()
232 y1 = (y-ymin)/(ymax-ymin+0.0002)
233 if y < 1.0: y = 1.0
234 if self.ylog_scale:
235 y1 = (log(y)-log(ymin))/(log(ymax)-log(ymin)+2.e-9)
236 if y1 < 0.0: y1 = 0.0
237 y2 = min(y1+0.25, y1*0.1 + 0.9)
238 if self.cursor_markers[idx] is not None:
239 try:
240 self.cursor_markers[idx].remove()
241 except:
242 pass
243 self.cursor_markers[idx] = axes.axvline(x, y1, y2, linewidth=2.5,
244 color=self.conf.marker_color)
246 if self.xmarker_left is not None:
247 ix = self.xmarker_left
248 x, y = self.xdata[ix], self.ydata[ix]
249 draw_ymarker_range(0, x, y)
250 self.write_message(fmt("L", ix, x, y), panel=1)
251 if self.xmarker_right is not None:
252 ix = self.xmarker_right
253 x, y = self.xdata[ix], self.ydata[ix]
254 draw_ymarker_range(1, x, y)
255 self.write_message(fmt("R", ix, x, y), panel=2)
257 if self.mca is None:
258 return
260 if (self.xmarker_left is not None and
261 self.xmarker_right is not None):
262 self.ShowROIStatus(self.xmarker_left,
263 self.xmarker_right,
264 name='', panel=3)
266 if self.selected_roi is not None:
267 roi = self.selected_roi
268 left, right = roi.left, roi.right
269 self.ShowROIStatus(left, right, name=roi.name, panel=0)
270 self.ShowROIPatch(left, right)
272 def createPlotPanel(self):
273 """mca plot window"""
274 pan = PlotPanel(self, fontsize=7, axisbg='#FFFFFF',
275 with_data_process=False,
276 output_title='test.xrf',
277 messenger=self.write_message)
278 pan.SetSize((650, 350))
280 pan.conf.grid_color='#E5E5C0'
281 pan.conf.show_grid = False
282 pan.conf.canvas.figure.set_facecolor('#FCFCFE')
283 pan.conf.labelfont.set_size(6)
284 pan.conf.labelfont.set_size(6)
285 pan.onRightDown= partial(self.on_cursor, side='right')
286 pan.add_cursor_mode('zoom', motion = self.ignoreEvent,
287 leftup = self.ignoreEvent,
288 leftdown = self.on_cursor,
289 rightdown = partial(self.on_cursor, side='right'))
290 return pan
292 def createControlPanel(self):
293 ctrlpanel = wx.Panel(self, name='Ctrl Panel')
294 ptable_fontsize = 9
295 ptable = PeriodicTablePanel(ctrlpanel, onselect=self.onShowLines,
296 tooltip_msg='Select Element for KLM Lines',
297 fontsize=ptable_fontsize, size=(360, 180))
298 self.wids['ptable'] = ptable
299 self.font_fixedwidth = wx.Font(FONTSIZE_FW, wx.MODERN, wx.NORMAL, wx.NORMAL)
301 labstyle = wx.ALIGN_LEFT|wx.EXPAND
302 ctrlstyle = wx.ALIGN_LEFT
303 txtstyle=wx.ALIGN_LEFT|wx.ST_NO_AUTORESIZE|wx.TE_PROCESS_ENTER
304 Font9 = Font(9)
305 Font10 = Font(10)
306 Font11 = Font(11)
307 #
308 arrowpanel = wx.Panel(ctrlpanel)
309 ssizer = wx.BoxSizer(wx.HORIZONTAL)
310 for wname, dname in (('uparrow', 'up'),
311 ('leftarrow', 'left'),
312 ('rightarrow', 'right'),
313 ('downarrow', 'down')):
314 self.wids[wname] = wx.BitmapButton(arrowpanel, -1,
315 get_icon(wname),
316 style=wx.NO_BORDER)
317 self.wids[wname].Bind(wx.EVT_BUTTON,
318 partial(ptable.onKey, name=dname))
319 ssizer.Add(self.wids[wname], 0, wx.EXPAND|wx.ALL)
321 self.wids['holdbtn'] = wx.ToggleButton(arrowpanel, -1, 'Hold ',
322 size=(85, -1))
323 self.wids['holdbtn'].Bind(wx.EVT_TOGGLEBUTTON, self.onToggleHold)
324 self.wids['kseries'] = Check(arrowpanel, ' K ', action=self.onKLM)
325 self.wids['lseries'] = Check(arrowpanel, ' L ', action=self.onKLM)
326 self.wids['mseries'] = Check(arrowpanel, ' M ', action=self.onKLM)
328 ssizer.Add(self.wids['holdbtn'], 0, wx.EXPAND|wx.ALL, 2)
329 ssizer.Add(self.wids['kseries'], 0, wx.EXPAND|wx.ALL, 0)
330 ssizer.Add(self.wids['lseries'], 0, wx.EXPAND|wx.ALL, 0)
331 ssizer.Add(self.wids['mseries'], 0, wx.EXPAND|wx.ALL, 0)
332 pack(arrowpanel, ssizer)
334 # roi section...
335 rsizer = wx.GridBagSizer(4, 6)
336 roipanel = wx.Panel(ctrlpanel, name='ROI Panel')
337 self.wids['roilist'] = wx.ListBox(roipanel, size=(140, 150))
338 self.wids['roilist'].Bind(wx.EVT_LISTBOX, self.onROI)
339 self.wids['roilist'].SetMinSize((140, 150))
340 self.wids['roiname'] = wx.TextCtrl(roipanel, -1, '', size=(150, -1))
342 #
343 roibtns= wx.Panel(roipanel, name='ROIButtons')
344 zsizer = wx.BoxSizer(wx.HORIZONTAL)
345 z1 = Button(roibtns, 'Add', size=(70, 30), action=self.onNewROI)
346 z2 = Button(roibtns, 'Delete', size=(70, 30), action=self.onConfirmDelROI)
347 z3 = Button(roibtns, 'Rename', size=(70, 30), action=self.onRenameROI)
349 zsizer.Add(z1, 0, wx.EXPAND|wx.ALL, 0)
350 zsizer.Add(z2, 0, wx.EXPAND|wx.ALL, 0)
351 zsizer.Add(z3, 0, wx.EXPAND|wx.ALL, 0)
352 pack(roibtns, zsizer)
354 rt1 = txt(roipanel, ' Channels:', size=80, font=Font10)
355 rt2 = txt(roipanel, ' Energy:', size=80, font=Font10)
356 rt3 = txt(roipanel, ' Cen, Wid:', size=80, font=Font10)
357 m = ''
358 self.wids['roi_msg1'] = txt(roipanel, m, size=135, font=Font10)
359 self.wids['roi_msg2'] = txt(roipanel, m, size=135, font=Font10)
360 self.wids['roi_msg3'] = txt(roipanel, m, size=135, font=Font10)
362 rsizer.Add(txt(roipanel, ' Regions of Interest:', size=125, font=Font11),
363 (0, 0), (1, 3), labstyle)
364 rsizer.Add(self.wids['roiname'], (1, 0), (1, 3), labstyle)
365 rsizer.Add(roibtns, (2, 0), (1, 3), labstyle)
366 rsizer.Add(rt1, (3, 0), (1, 1), LEFT)
367 rsizer.Add(rt2, (4, 0), (1, 1), LEFT)
368 rsizer.Add(rt3, (5, 0), (1, 1), LEFT)
369 rsizer.Add(self.wids['roi_msg1'], (3, 1), (1, 2), labstyle)
370 rsizer.Add(self.wids['roi_msg2'], (4, 1), (1, 2), labstyle)
371 rsizer.Add(self.wids['roi_msg3'], (5, 1), (1, 2), labstyle)
372 rsizer.Add(self.wids['roilist'], (0, 3), (6, 1),
373 wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT)
374 rsizer.SetHGap(1)
376 pack(roipanel, rsizer)
377 # end roi section
379 # y scale
380 yscalepanel = wx.Panel(ctrlpanel, name='YScalePanel')
381 ysizer = wx.BoxSizer(wx.HORIZONTAL)
382 ytitle = txt(yscalepanel, ' Y Axis:', font=Font10, size=80)
383 yspace = txt(yscalepanel, ' ', font=Font10, size=20)
384 ylog = Choice(yscalepanel, size=(80, 30), choices=['log', 'linear'],
385 action=self.onLogLinear)
386 yaxis = Check(yscalepanel, ' Show Y Scale ', action=self.onYAxis,
387 default=False)
388 self.wids['show_yaxis'] = yaxis
389 ysizer.Add(ytitle, 0, wx.ALL, 0)
390 ysizer.Add(ylog, 0, wx.EXPAND|wx.ALL, 0)
391 ysizer.Add(yspace, 0, wx.EXPAND|wx.ALL, 0)
392 ysizer.Add(yaxis, 0, wx.EXPAND|wx.ALL, 0)
393 pack(yscalepanel, ysizer)
395 # zoom buttons
396 zoompanel = wx.Panel(ctrlpanel, name='ZoomPanel')
397 zsizer = wx.BoxSizer(wx.HORIZONTAL)
398 z1 = Button(zoompanel, 'Zoom In', size=(80, 30), action=self.onZoomIn)
399 z2 = Button(zoompanel, 'Zoom Out', size=(80, 30), action=self.onZoomOut)
400 p1 = Button(zoompanel, 'Pan Lo', size=(75, 30), action=self.onPanLo)
401 p2 = Button(zoompanel, 'Pan Hi', size=(75, 30), action=self.onPanHi)
403 zsizer.Add(p1, 0, wx.EXPAND|wx.ALL, 0)
404 zsizer.Add(p2, 0, wx.EXPAND|wx.ALL, 0)
405 zsizer.Add(z1, 0, wx.EXPAND|wx.ALL, 0)
406 zsizer.Add(z2, 0, wx.EXPAND|wx.ALL, 0)
407 pack(zoompanel, zsizer)
409 self.wids['xray_lines'] = None
411 dvstyle = dv.DV_SINGLE|dv.DV_VERT_RULES|dv.DV_ROW_LINES
412 xlines = dv.DataViewListCtrl(ctrlpanel, style=dvstyle)
413 xlines.SetFont(self.font_fixedwidth)
414 self.wids['xray_lines'] = xlines
416 xw = (55, 105, 90, 95)
417 xlines.AppendTextColumn('Line', width=xw[0])
418 xlines.AppendTextColumn('Energy(keV)', width=xw[1])
419 xlines.AppendTextColumn('Strength', width=xw[2])
420 xlines.AppendTextColumn('Levels', width=xw[3])
421 for col in (0, 1, 2, 3):
422 this = xlines.Columns[col]
423 this.Sortable = False
424 align = RIGHT
425 if col in (0, 3):
426 align = wx.ALIGN_LEFT
427 this.Alignment = this.Renderer.Alignment = align
429 xlines.SetMinSize((300, 240))
430 xlines.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED,
432 self.onSelectXrayLine)
433 store = xlines.GetStore()
435 # main layout
436 # may have to adjust comparison....
438 sizer = wx.BoxSizer(wx.VERTICAL)
439 sizer.Add(roipanel, 0, labstyle)
440 sizer.Add(lin(ctrlpanel, 195), 0, labstyle)
441 sizer.Add(yscalepanel, 0, wx.EXPAND|wx.ALL)
442 sizer.Add(zoompanel, 0, wx.EXPAND|wx.ALL)
443 sizer.Add(lin(ctrlpanel, 195), 0, labstyle)
444 sizer.Add(ptable, 0, wx.EXPAND|wx.ALL, 4)
445 sizer.Add(arrowpanel, 0, labstyle)
446 sizer.Add(lin(ctrlpanel, 195), 0, labstyle)
448 if self.wids['xray_lines'] is not None:
449 sizer.Add(xlines, 0, wx.GROW|wx.ALL|wx.EXPAND)
451 pack(ctrlpanel, sizer)
452 return ctrlpanel
454 def createMainPanel(self):
455 ctrlpanel = self.createControlPanel()
456 plotpanel = self.panel = self.createPlotPanel()
457 plotpanel.yformatter = self._formaty
459 tx, ty = self.wids['ptable'].GetBestVirtualSize()
460 cx, cy = ctrlpanel.GetBestVirtualSize()
461 px, py = plotpanel.GetBestVirtualSize() # (650, 350)
462 self.SetSize((max(cx, tx)+px, 25+max(cy, py)))
464 style = wx.ALIGN_LEFT|wx.EXPAND|wx.ALL
465 sizer = wx.BoxSizer(wx.HORIZONTAL)
466 sizer.Add(ctrlpanel, 0, style, 3)
467 sizer.Add(plotpanel, 1, style, 2)
469 self.SetMinSize((450, 150))
470 pack(self, sizer)
471 self.set_roilist(mca=None)
473 def init_larch(self):
474 symtab = self.larch.symtable
475 if not symtab.has_symbol('_sys.wx.wxapp'):
476 symtab.set_symbol('_sys.wx.wxapp', wx.GetApp())
477 if not symtab.has_symbol('_sys.wx.parent'):
478 symtab.set_symbol('_sys.wx.parent', self)
480 if not symtab.has_group(XRFGROUP):
481 self.larch.eval(MAKE_XRFGROUP_CMD)
483 fico = Path(icondir, ICON_FILE).as_posix()
484 try:
485 self.SetIcon(wx.Icon(fico, wx.BITMAP_TYPE_ICO))
486 except:
487 pass
489 def add_mca(self, mca, filename=None, label=None, as_mca2=False, plot=True):
490 if as_mca2:
491 self.mca2 = mca
492 else:
493 self.mca2 = self.mca
494 self.mca = mca
495 # print("Add MCA ", mca, as_mca2)
496 xrfgroup = self.larch.symtable.get_group(XRFGROUP)
497 mcaname = next_mcaname(self.larch)
498 if filename is not None:
499 if isinstance(filename, Path):
500 filename = Path(filename).absolute().as_posix()
501 self.larch.eval(read_mcafile.format(group=XRFGROUP,
502 name=mcaname,
503 filename=filename))
504 if label is None:
505 label = filename
506 if label is None and hasattr(mca, 'filename'):
507 label = mca.filename
508 if label is None:
509 label = mcaname
510 self.mca.label = label
511 # push mca to mca2, save id of this mca
512 setattr(xrfgroup, '_mca2', getattr(xrfgroup, '_mca', ''))
513 setattr(xrfgroup, '_mca', mcaname)
514 setattr(xrfgroup, mcaname, mca)
515 # print("Add MCA ", xrfgroup, mcaname)
516 if plot:
517 self.plotmca(self.mca)
518 if as_mca2:
519 self.plotmca(self.mca, as_mca2=True)
521 def _getlims(self):
522 emin, emax = self.panel.axes.get_xlim()
523 erange = emax-emin
524 emid = (emax+emin)/2.0
525 if self.energy_for_zoom is not None:
526 emid = self.energy_for_zoom
527 dmin, dmax = emin, emax
528 drange = erange
529 if self.mca is not None:
530 dmin, dmax = self.mca.energy.min(), self.mca.energy.max()
531 return (emid, erange, dmin, dmax)
533 def _set_xview(self, e1, e2, keep_zoom=False):
534 if not keep_zoom:
535 self.energy_for_zoom = (e1+e2)/2.0
536 self.panel.axes.set_xlim((e1, e2))
537 self.xview_range = [e1, e2]
538 self.draw()
540 def onPanLo(self, event=None):
541 emid, erange, dmin, dmax = self._getlims()
542 e1 = max(dmin, emid-0.9*erange)
543 e2 = min(dmax, e1 + erange)
544 self._set_xview(e1, e2)
546 def onPanHi(self, event=None):
547 emid, erange, dmin, dmax = self._getlims()
548 e2 = min(dmax, emid+0.9*erange)
549 e1 = max(dmin, e2-erange)
550 self._set_xview(e1, e2)
552 def onZoomIn(self, event=None):
553 emid, erange, dmin, dmax = self._getlims()
554 e1 = max(dmin, emid-erange/3.0)
555 e2 = min(dmax, emid+erange/3.0)
556 self._set_xview(e1, e2, keep_zoom=True)
558 def onZoomOut(self, event=None):
559 emid, erange, dmin, dmax = self._getlims()
560 e1 = max(dmin, emid-1.25*erange)
561 e2 = min(dmax, emid+1.25*erange)
562 self._set_xview(e1, e2)
564 def unzoom_all(self, event=None):
565 emid, erange, dmin, dmax = self._getlims()
566 self._set_xview(dmin, dmax)
567 self.xview_range = None
569 def toggle_grid(self, event=None):
570 self.panel.toggle_grid()
572 def set_roilist(self, mca=None):
573 """ Add Roi names to roilist"""
574 self.wids['roilist'].Clear()
575 if mca is not None:
576 for roi in mca.rois:
577 name = bytes2str(roi.name.strip())
578 if len(name) > 0:
579 self.wids['roilist'].Append(roi.name)
581 def clear_roihighlight(self, event=None):
582 self.selected_roi = None
583 try:
584 self.roi_patch.remove()
585 except:
586 pass
587 self.roi_patch = None
588 self.wids['roiname'].SetValue('')
589 self.draw()
591 def get_roiname(self):
592 roiname = self.wids['roiname'].GetValue()
593 if len(roiname) < 1:
594 roiname = 'ROI 1'
595 names = [str(r.name.lower()) for r in self.mca.rois]
596 if str(roiname.lower()) in names:
597 ix = 1
598 while str(roiname.lower()) in names:
599 roiname = "ROI %i" % (ix)
600 ix += 1
601 return roiname
603 def onNewROI(self, event=None):
604 if (self.xmarker_left is None or
605 self.xmarker_right is None or self.mca is None):
606 return
607 roiname = self.get_roiname()
609 names = [str(r.name.lower()) for r in self.mca.rois]
610 if str(roiname.lower()) in names:
611 msg = "Overwrite Definition of ROI {:s}?".format(roiname)
612 if (wx.ID_YES != Popup(self, msg, 'Overwrite ROI?', style=wx.YES_NO)):
613 return False
615 left, right = self.xmarker_left, self.xmarker_right
616 if left > right:
617 left, right = right, left
618 self.mca.add_roi(name=roiname, left=left, right=right, sort=True)
619 self.set_roilist(mca=self.mca)
620 for roi in self.mca.rois:
621 if roi.name.lower()==roiname:
622 selected_roi = roi
623 self.plot(self.xdata, self.ydata)
624 self.onROI(label=roiname)
625 if self.selected_elem is not None:
626 self.onShowLines(elem=self.selected_elem)
627 if self.roi_callback is not None:
628 xrange = [self.mca.energy[left], self.mca.energy[right]]
629 self.roi_callback(roiname, xrange=xrange, action='add', units='keV', roitype='XRF')
630 return True
632 def onConfirmDelROI(self, event=None):
633 roiname = self.wids['roiname'].GetValue()
634 msg = "Delete ROI {:s}?".format(roiname)
635 if (wx.ID_YES == Popup(self, msg, 'Delete ROI?', style=wx.YES_NO)):
636 self.onDelROI()
637 if self.roi_callback is not None:
638 self.roi_callback(roiname, action='delete', roitype='XRF')
640 def onRenameROI(self, event=None):
641 roiname = self.get_roiname()
643 if self.roilist_sel is not None:
644 names = self.wids['roilist'].GetStrings()
645 names[self.roilist_sel] = roiname
646 self.wids['roilist'].Clear()
647 for sname in names:
648 self.wids['roilist'].Append(sname)
649 self.wids['roilist'].SetSelection(self.roilist_sel)
651 def onDelROI(self):
652 roiname = self.wids['roiname'].GetValue()
653 rdat = []
654 if self.mca is None:
655 return
656 for i in range(len(self.mca.rois)):
657 roi = self.mca.rois.pop(0)
658 if roi.name.lower() != roiname.lower():
659 rdat.append((roi.name, roi.left, roi.right))
661 for name, left, right in rdat:
662 self.mca.add_roi(name=name, left=left, right=right, sort=False)
663 self.mca.rois.sort()
664 self.set_roilist(mca=self.mca)
665 self.wids['roiname'].SetValue('')
666 try:
667 self.roi_patch.remove()
668 except:
669 pass
671 self.plot(self.xdata, self.ydata)
672 if self.selected_elem is not None:
673 self.onShowLines(elem=self.selected_elem)
675 def ShowROIStatus(self, left, right, name='', panel=0):
676 if left > right:
677 return
678 sum = self.ydata[left:right].sum()
679 dt = self.mca.real_time
680 nmsg, cmsg, rmsg = '', '', ''
681 if len(name) > 0:
682 nmsg = " %s" % name
683 cmsg = " Cts={:10,.0f}".format(sum)
684 if dt is not None and dt > 1.e-9:
685 rmsg = " CPS={:10,.1f}".format(sum/dt)
686 self.write_message("%s%s%s" % (nmsg, cmsg, rmsg), panel=panel)
688 def ShowROIPatch(self, left, right):
689 """show colored XRF Patch:
690 Note: ROIs larger than half the energy are not colored"""
691 # xnpts = 1.0/len(self.mca.energy)
692 # if xnpts*(right - left) > 0.5:
693 # return
695 try:
696 self.roi_patch.remove()
697 except:
698 pass
700 e = np.zeros(right-left+2)
701 r = np.ones(right-left+2)
702 e[1:-1] = self.mca.energy[left:right]
703 r[1:-1] = self.mca.counts[left:right]
704 e[0] = e[1]
705 e[-1] = e[-2]
706 self.roi_patch = self.panel.axes.fill_between(e, r, zorder=-20,
707 color=self.conf.roi_fillcolor)
709 def onROI(self, event=None, label=None):
710 if label is None and event is not None:
711 label = event.GetString()
712 self.roilist_sel = event.GetSelection()
714 self.wids['roiname'].SetValue(label)
715 name, left, right= None, -1, -1
716 label = bytes2str(label.lower().strip())
718 self.selected_roi = None
719 if self.mca is not None:
720 for roi in self.mca.rois:
721 if bytes2str(roi.name.lower())==label:
722 left, right, name = roi.left, roi.right, roi.name
723 elo = self.mca.energy[left]
724 ehi = self.mca.energy[right]
725 self.selected_roi = roi
726 break
727 if name is None or right == -1:
728 return
730 self.ShowROIStatus(left, right, name=name)
731 self.ShowROIPatch(left, right)
733 roi_msg1 = '[{:}:{:}]'.format(left, right)
734 roi_msg2 = '[{:6.3f}:{:6.3f}]'.format(elo, ehi)
735 roi_msg3 = '{:6.3f}, {:6.3f}'.format((elo+ehi)/2., (ehi - elo))
737 self.energy_for_zoom = (elo+ehi)/2.0
739 self.wids['roi_msg1'].SetLabel(roi_msg1)
740 self.wids['roi_msg2'].SetLabel(roi_msg2)
741 self.wids['roi_msg3'].SetLabel(roi_msg3)
743 self.draw()
744 self.panel.Refresh()
746 def onSaveROIs(self, event=None):
747 pass
749 def onRestoreROIs(self, event=None):
750 pass
752 def createCustomMenus(self):
753 return
755 def createBaseMenus(self):
756 fmenu = wx.Menu()
757 MenuItem(self, fmenu, "&Read MCA Spectra File\tCtrl+O",
758 "Read GSECARS MCA File", self.onReadMCAFile)
760 MenuItem(self, fmenu, "&Save MCA File\tCtrl+S",
761 "Save GSECARS MCA File", self.onSaveMCAFile)
762 MenuItem(self, fmenu, "&Save ASCII Column File\tCtrl+A",
763 "Save Column File", self.onSaveColumnFile)
765 MenuItem(self, fmenu, "&Inspect \tCtrl+J",
766 " wx inspection tool ", self.showInspectionTool)
767 fmenu.AppendSeparator()
768 # MenuItem(self, fmenu, "Save ROIs to File",
769 # "Save ROIs to File", self.onSaveROIs)
770 # MenuItem(self, fmenu, "Restore ROIs File",
771 # "Read ROIs from File", self.onRestoreROIs)
772 # fmenu.AppendSeparator()
773 MenuItem(self, fmenu, 'Show Larch Buffer\tCtrl+L',
774 'Show Larch Programming Buffer',
775 self.onShowLarchBuffer)
776 MenuItem(self, fmenu, "Save Plot\tCtrl+I",
777 "Save PNG Image of Plot", self.onSavePNG)
778 MenuItem(self, fmenu, "&Copy Plot\tCtrl+C",
779 "Copy Plot Image to Clipboard",
780 self.onCopyImage)
781 MenuItem(self, fmenu, 'Page Setup...', 'Printer Setup', self.onPageSetup)
782 MenuItem(self, fmenu, 'Print Preview...', 'Print Preview', self.onPrintPreview)
783 MenuItem(self, fmenu, "&Print\tCtrl+P", "Print Plot", self.onPrint)
785 fmenu.AppendSeparator()
786 MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose)
788 omenu = wx.Menu()
789 MenuItem(self, omenu, "Configure Colors",
790 "Configure Colors", self.config_colors)
791 MenuItem(self, omenu, "Configure X-ray Lines",
792 "Configure which X-ray Lines are shown", self.config_xraylines)
793 MenuItem(self, omenu, "Configure Plot\tCtrl+K",
794 "Configure Plot Colors, etc", self.panel.configure)
795 MenuItem(self, omenu, "Zoom Out\tCtrl+Z",
796 "Zoom out to full data range", self.unzoom_all)
797 MenuItem(self, omenu, "Toggle Grid\tCtrl+G",
798 "Toggle Grid Display", self.toggle_grid)
799 MenuItem(self, omenu, "Toggle Plot legend",
800 "Toggle Plot Legend", self.onToggleLegend)
801 omenu.AppendSeparator()
802 MenuItem(self, omenu, "Hide X-ray Lines",
803 "Hide all X-ray Lines", self.clear_lines)
804 MenuItem(self, omenu, "Hide selected ROI ",
805 "Hide selected ROI", self.clear_roihighlight)
806 MenuItem(self, omenu, "Hide Markers ",
807 "Hide cursor markers", self.clear_markers)
808 MenuItem(self, omenu, "Hide XRF Background ",
809 "Hide cursor markers", self.clear_background)
811 omenu.AppendSeparator()
812 MenuItem(self, omenu, "Swap MCA and Background MCA",
813 "Swap Foreground and Background MCAs", self.swap_mcas)
814 MenuItem(self, omenu, "Close Background MCA",
815 "Close Background MCA", self.close_bkg_mca)
817 amenu = wx.Menu()
818 MenuItem(self, amenu, "Show Pileup Prediction",
819 "Show Pileup Prediction", kind=wx.ITEM_CHECK,
820 checked=False, action=self.onPileupPrediction)
821 MenuItem(self, amenu, "Show Escape Prediction",
822 "Show Escape Prediction", kind=wx.ITEM_CHECK,
823 checked=False, action=self.onEscapePrediction)
824 MenuItem(self, amenu, "&Calibrate Energy\tCtrl+E",
825 "Calibrate Energy", self.onCalibrateEnergy)
826 MenuItem(self, amenu, "Fit Spectrum\tCtrl+F",
827 "Fit Spectrum for Elemental Contributiosn",
828 self.onFitSpectrum)
829 self._menus = [(fmenu, '&File'),
830 (omenu, '&Options'),
831 (amenu, '&Analysis')]
833 def createMenus(self):
834 self.menubar = wx.MenuBar()
835 self.createBaseMenus()
836 self.createCustomMenus()
837 for menu, title in self._menus:
838 self.menubar.Append(menu, title)
839 self.SetMenuBar(self.menubar)
840 self.Bind(wx.EVT_CLOSE, self.onClose)
842 def onShowLarchBuffer(self, evt=None):
843 if self.larch_buffer is not None:
844 self.larch_buffer.Show()
845 self.larch_buffer.Raise()
847 def onSavePNG(self, event=None):
848 if self.panel is not None:
849 self.panel.save_figure(event=event)
851 def onCopyImage(self, event=None):
852 if self.panel is not None:
853 self.panel.canvas.Copy_to_Clipboard(event=event)
855 def onPageSetup(self, event=None):
856 if self.panel is not None:
857 self.panel.PrintSetup(event=event)
859 def onPrintPreview(self, event=None):
860 if self.panel is not None:
861 self.panel.PrintPreview(event=event)
863 def onPrint(self, event=None):
864 if self.panel is not None:
865 self.panel.Print(event=event)
867 def onClose(self, event=None):
868 try:
869 if callable(self.exit_callback):
870 self.exit_callback()
871 except:
872 pass
873 try:
874 if self.panel is not None:
875 self.panel.win_config.Close(True)
876 if self.panel is not None:
877 self.panel.win_config.Destroy()
878 except:
879 pass
881 if hasattr(self.larch.symtable, '_plotter'):
882 wx.CallAfter(self.larch.symtable._plotter.close_all_displays)
884 for name, wid in self.subframes.items():
885 if hasattr(wid, 'Destroy'):
886 wx.CallAfter(wid.Destroy)
887 self.Destroy()
889 def config_colors(self, event=None):
890 """show configuration frame"""
891 try:
892 self.win_config.Raise()
893 except:
894 self.win_config = ColorsFrame(parent=self)
896 def config_xraylines(self, event=None):
897 """show configuration frame"""
898 try:
899 self.win_config.Raise()
900 except:
901 self.win_config = XrayLinesFrame(parent=self)
903 def onToggleLegend(self, event=None):
904 self.panel.conf.show_legend = not self.panel.conf.show_legend
905 self.panel.conf.draw_legend()
907 def onKLM(self, event=None):
908 """selected K, L, or M Markers"""
909 if self.selected_elem is not None:
910 self.onShowLines(elem = self.selected_elem)
912 def onToggleHold(self, event=None):
913 if event.IsChecked():
914 self.wids['holdbtn'].SetLabel("Hide %s" % self.selected_elem)
915 self.hold_lines = self.saved_lines[:]
916 else:
917 self.wids['holdbtn'].SetLabel("Hold %s" % self.selected_elem)
918 self.hold_lines = None
919 for m in self.hold_markers:
920 try:
921 m.remove()
922 except:
923 pass
924 self.hold_markers = []
925 self.draw()
927 def onSelectXrayLine(self, evt=None):
928 if self.wids['xray_lines'] is None:
929 return
930 if not self.wids['xray_lines'].HasSelection():
931 return
932 item = self.wids['xray_lines'].GetSelectedRow()
933 en = self.wids['xray_linesdata'][item]
935 if self.highlight_xrayline is not None:
936 self.highlight_xrayline.remove()
938 self.energy_for_zoom = en
939 self.highlight_xrayline = self.panel.axes.axvline(en,
940 color=self.conf.emph_elinecolor,
941 linewidth=2.5, zorder=-15)
942 self.draw()
944 def onShowLines(self, event=None, elem=None):
945 if elem is None:
946 elem = event.GetString()
948 vline = self.panel.axes.axvline
949 elines = self.larch.symtable._xray.xray_lines(elem)
951 self.selected_elem = elem
952 self.clear_lines()
953 self.energy_for_zoom = None
954 xlines = self.wids['xray_lines']
955 if xlines is not None:
956 xlines.DeleteAllItems()
957 self.wids['xray_linesdata'] = []
958 minors, majors = [], []
959 conf = self.conf
960 line_data = {}
961 for line in (conf.K_major+conf.K_minor+conf.L_major+
962 conf.L_minor+conf.M_major):
963 line_data[line] = line, -1, 0, '', ''
964 if line in elines:
965 dat = elines[line]
966 line_data[line] = line, dat[0], dat[1], dat[2], dat[3]
968 if self.wids['kseries'].IsChecked():
969 majors.extend([line_data[l] for l in conf.K_major])
970 minors.extend([line_data[l] for l in conf.K_minor])
971 if self.wids['lseries'].IsChecked():
972 majors.extend([line_data[l] for l in conf.L_major])
973 minors.extend([line_data[l] for l in conf.L_minor])
974 if self.wids['mseries'].IsChecked():
975 majors.extend([line_data[l] for l in conf.M_major])
977 self.saved_lines = majors[:] + minors[:]
978 erange = [max(conf.e_min, self.xdata.min()),
979 min(conf.e_max, self.xdata.max())]
981 view_mid, view_range, d1, d2 = self._getlims()
982 view_emin = view_mid - view_range/2.0
983 view_emax = view_mid + view_range/2.0
984 for label, eev, frac, ilevel, flevel in majors:
985 e = float(eev) * 0.001
986 # print( 'Major ', label, eev, e, frac, ilevel, flevel)
987 if (e >= erange[0] and e <= erange[1]):
988 l = vline(e, color= self.conf.major_elinecolor,
989 linewidth=1.50, zorder=-5)
990 l.set_label(label)
991 dat = (label, "%.4f" % e, "%.4f" % frac,
992 "%s->%s" % (ilevel, flevel))
993 self.wids['xray_linesdata'].append(e)
994 if xlines is not None:
995 xlines.AppendItem(dat)
997 self.major_markers.append(l)
998 if (self.energy_for_zoom is None and
999 e > view_emin and e < view_emax):
1000 self.energy_for_zoom = e
1002 for label, eev, frac, ilevel, flevel in minors:
1003 e = float(eev) * 0.001
1004 if (e >= erange[0] and e <= erange[1]):
1005 l = vline(e, color= self.conf.minor_elinecolor,
1006 linewidth=1.25, zorder=-7)
1007 l.set_label(label)
1009 # dat = (label, "%.4f" % e, "%.4f" % frac,
1010 # "%s->%s" % (ilevel, flevel))
1011 dat = (label, "%.4f" % e, "%.4f" % frac,
1012 "%s->%s" % (ilevel, flevel))
1014 self.wids['xray_linesdata'].append(e)
1015 if xlines is not None:
1016 xlines.AppendItem(dat)
1017 self.minor_markers.append(l)
1019 if not self.wids['holdbtn'].GetValue():
1020 self.wids['holdbtn'].SetLabel("Hold %s" % elem)
1021 elif self.hold_lines is not None:
1022 for label, eev, frac, ilevel, flevel in self.hold_lines:
1023 e = float(eev) * 0.001
1024 if (e >= erange[0] and e <= erange[1]):
1025 l = vline(e, color=self.conf.hold_elinecolor,
1026 linewidth=1.5, zorder=-20, dashes=(3, 3))
1027 l.set_label(label)
1028 self.hold_markers.append(l)
1030 if xlines is not None:
1031 xlines.Refresh()
1033 edge_en = {}
1034 for edge in ('K', 'M5', 'L3', 'L2', 'L1'):
1035 edge_en[edge] = None
1036 xex = self.larch.symtable._xray.xray_edge(elem, edge)
1037 if xex is not None:
1038 en = xex[0]*0.001
1039 if en > erange[0] and en < erange[1]:
1040 edge_en[edge] = en
1041 out = ''
1042 for key in ('M5', 'K'):
1043 if edge_en[key] is not None:
1044 out = "%s=%.3f" % (key, edge_en[key])
1045 if len(out) > 1:
1046 self.wids['ptable'].set_subtitle(out, index=0)
1047 s, v, out = [], [], ''
1048 for key in ('L3', 'L2', 'L1'):
1049 if edge_en[key] is not None:
1050 s.append(key)
1051 v.append("%.3f" % edge_en[key])
1052 if len(s) > 0:
1053 out = "%s=%s" %(', '.join(s), ', '.join(v))
1054 self.wids['ptable'].set_subtitle(out, index=1)
1056 self.draw()
1058 def onPileupPrediction(self, event=None):
1059 if event.IsChecked():
1060 self.mca.predict_pileup()
1061 self.oplot(self.mca.energy, self.mca.pileup,
1062 color=self.conf.pileup_color, label='pileup prediction')
1063 else:
1064 self.plotmca(self.mca)
1066 def onEscapePrediction(self, event=None):
1067 if event.IsChecked():
1068 self.mca.predict_escape()
1069 self.oplot(self.mca.energy, self.mca.escape,
1070 color=self.conf.escape_color, label='escape prediction')
1071 else:
1072 self.plotmca(self.mca)
1075 def onYAxis(self, event=None):
1076 self.show_yaxis = self.wids['show_yaxis'].IsChecked()
1077 ax = self.panel.axes
1078 ax.yaxis.set_major_formatter(FuncFormatter(self._formaty))
1079 ax.get_yaxis().set_visible(self.show_yaxis)
1080 ax.spines['right'].set_visible(False)
1081 ax.yaxis.set_ticks_position('left')
1082 self.draw()
1084 def _formaty(self, val, index=0, **kws):
1085 try:
1086 decade = int(np.log10(val))
1087 except:
1088 decade = 0
1089 scale = 10**decade
1090 out = "%.1fe%i" % (val/scale, decade)
1091 if abs(decade) < 1.9:
1092 out = "%.1f" % val
1093 elif abs(decade) < 3.9:
1094 out = "%.0f" % val
1095 return out
1097 def onLogLinear(self, event=None):
1098 self.ylog_scale = 'log' == event.GetString()
1099 roiname = None
1100 if self.selected_roi is not None:
1101 roiname = self.selected_roi.name
1102 self.plot(self.xdata, self.ydata)
1103 if self.selected_elem is not None:
1104 self.onShowLines(elem=self.selected_elem)
1105 if roiname is not None:
1106 self.onROI(label=roiname)
1107 if self.y2data is not None:
1108 self.oplot(self.x2data, self.y2data)
1110 def plotmca(self, mca, title=None, set_title=True, as_mca2=False,
1111 fullrange=False, init=False, **kws):
1112 if as_mca2:
1113 self.mca2 = mca
1114 kws['new'] = False
1115 else:
1116 self.mca = mca
1117 self.panel.conf.show_grid = False
1118 xview_range = self.panel.axes.get_xlim()
1120 if init or xview_range == (0.0, 1.0):
1121 self.xview_range = (min(self.mca.energy), max(self.mca.energy))
1122 else:
1123 self.xview_range = xview_range
1125 atitles = []
1126 if self.mca is not None:
1127 if getattr(self.mca, 'title', None) is not None:
1128 atitles.append(bytes2str(self.mca.title))
1129 if getattr(self.mca, 'filename', None) is not None:
1130 atitles.append(" File={:s}".format(self.mca.filename))
1131 if getattr(self.mca, 'npixels', None) is not None:
1132 atitles.append(" {:.0f} Pixels".format(self.mca.npixels))
1133 if getattr(self.mca, 'real_time', None) is not None:
1134 try:
1135 rtime_str = " RealTime={:.2f} sec".format(self.mca.real_time)
1136 except ValueError:
1137 rtime_str = " RealTime= %s sec".format(str(self.mca.real_time))
1138 atitles.append(rtime_str)
1140 try:
1141 self.plot(self.mca.energy, self.mca.counts,
1142 mca=self.mca, **kws)
1143 except ValueError:
1144 pass
1145 if as_mca2:
1146 if getattr(self.mca2, 'title', None) is not None:
1147 atitles.append(" BG={:s}".format(self.mca2.title))
1148 elif getattr(self.mca2, 'filename', None) is not None:
1149 atitles.append(" BG_File={:s}".format(self.mca2.filename))
1150 if getattr(self.mca, 'real_time', None) is not None:
1151 atitles.append(" BG_RealTime={:.2f} sec".format(self.mca2.real_time))
1153 self.oplot(self.mca2.energy, self.mca2.counts,
1154 mca=self.mca2, **kws)
1155 if title is None:
1156 title = ' '.join(atitles)
1157 if set_title:
1158 self.SetTitle(title)
1160 def plot(self, x, y=None, mca=None, init=False, with_rois=True, **kws):
1161 if mca is not None:
1162 self.mca = mca
1163 mca = self.mca
1164 panel = self.panel
1166 panel.yformatter = self._formaty
1167 panel.axes.get_yaxis().set_visible(False)
1168 kwargs = {'xmin': 0,
1169 'linewidth': 2.5,
1170 'delay_draw': True,
1171 'grid': panel.conf.show_grid,
1172 'ylog_scale': self.ylog_scale,
1173 'xlabel': 'E (keV)',
1174 'axes_style': 'bottom',
1175 'color': self.conf.spectra_color}
1176 kwargs.update(kws)
1178 self.xdata = 1.0*x[:]
1179 self.ydata = 1.0*y[:]
1180 self.ydata[np.where(self.ydata<1.e-9)] = 1.e-9
1181 ydat = self.ydata
1182 kwargs['ymax'] = max(ydat)*1.25
1183 kwargs['ymin'] = 0.9
1184 kwargs['xmax'] = max(self.xdata)
1185 kwargs['xmin'] = min(self.xdata)
1186 if self.xview_range is not None:
1187 kwargs['xmin'] = self.xview_range[0]
1188 kwargs['xmax'] = self.xview_range[1]
1190 panel.plot(x, ydat, label='spectrum', **kwargs)
1191 if with_rois and mca is not None:
1192 if not self.rois_shown:
1193 self.set_roilist(mca=mca)
1194 yroi = -1.0*np.ones(len(y))
1195 max_width = 0.5*len(self.mca.energy) # suppress very large ROIs
1196 for r in mca.rois:
1197 if ((r.left, r.right) in ((0, 0), (-1, -1)) or
1198 (r.right - r.left) > max_width):
1199 continue
1200 yroi[r.left:r.right] = y[r.left:r.right]
1201 yroi = np.ma.masked_less(yroi, 0)
1202 xroi = 1.0*x[:]
1203 xroi[yroi< 0.0] = np.nan
1204 if yroi.max() > 0:
1205 kwargs['color'] = self.conf.roi_color
1206 panel.oplot(xroi, yroi, label='rois', **kwargs)
1207 yscale = {False:'linear', True:'log'}[self.ylog_scale]
1208 panel.set_viewlimits()
1209 panel.set_logscale(yscale=yscale)
1210 panel.axes.get_yaxis().set_visible(self.show_yaxis)
1211 panel.cursor_mode = 'zoom'
1212 self.draw()
1213 panel.canvas.Refresh()
1216 def update_mca(self, counts, energy=None, with_rois=True,
1217 is_mca2=False, draw=True):
1218 """update counts (and optionally energy) for mca, and update plot"""
1219 mca = self.mca
1220 ix = 0
1221 if is_mca2:
1222 mca = self.mca2
1223 ix = 2
1224 mca.counts = 1.0*counts[:]
1225 if energy is not None:
1226 mca.energy = 1.0*energy[:]
1227 xnpts = 1.0/len(energy)
1228 nrois = len(mca.rois)
1229 if not is_mca2 and with_rois and nrois > 0:
1230 yroi = -1*np.ones(len(counts))
1231 for r in mca.rois:
1232 if xnpts*(r.right - r.left) > 0.5:
1233 continue
1234 yroi[r.left:r.right] = counts[r.left:r.right]
1235 yroi = np.ma.masked_less(yroi, 0)
1236 self.panel.update_line(1, mca.energy, yroi, draw=False,
1237 update_limits=False)
1239 self.panel.update_line(ix, mca.energy, counts,
1240 draw=False, update_limits=False)
1242 max_counts = max_counts2 = max(self.mca.counts)
1243 try:
1244 max_counts2 = max(self.mca2.counts)
1245 except:
1246 pass
1248 self.panel.axes.set_ylim(0.9, 1.25*max(max_counts, max_counts2))
1249 if mca == self.mca:
1250 self.ydata = 1.0*counts[:]
1251 self.update_status()
1252 if draw: self.draw()
1254 def oplot(self, x, y, color='darkgreen', label='spectrum2',
1255 mca=None, zorder=-2, **kws):
1256 if mca is not None:
1257 self.mca2 = mca
1259 self.x2data = 1.0*x[:]
1260 self.y2data = 1.0*y[:]
1261 if hasattr(self, 'ydata'):
1262 ymax = max(max(self.ydata), max(y))*1.25
1263 else:
1264 ymax = max(y)*1.25
1266 kws.update({'zorder': zorder, 'label': label,
1267 'ymax' : ymax, 'axes_style': 'bottom',
1268 'ylog_scale': self.ylog_scale})
1269 self.panel.oplot(self.x2data, self.y2data, color=color, **kws)
1271 def swap_mcas(self, event=None):
1272 if self.mca2 is None:
1273 return
1274 self.mca, self.mca2 = self.mca2, self.mca
1275 xrfgroup = self.larch.symtable.get_group(XRFGROUP)
1276 _mca = getattr(xrfgroup, '_mca', '')
1277 _mca2 = getattr(xrfgroup, '_mca2', '')
1278 setattr(xrfgroup, '_mca2', _mca)
1279 setattr(xrfgroup, '_mca', _mca2)
1281 self.plotmca(self.mca)
1282 self.plotmca(self.mca2, as_mca2=True)
1284 def close_bkg_mca(self, event=None):
1285 self.mca2 = None
1286 xrfgroup = self.larch.symtable.get_group(XRFGROUP)
1287 setattr(xrfgroup, '_mca2', '')
1288 self.plotmca(self.mca)
1290 def onReadMCAFile(self, event=None):
1291 dlg = wx.FileDialog(self, message="Open MCA File for reading",
1292 defaultDir=get_cwd(),
1293 wildcard=FILE_WILDCARDS,
1294 style = wx.FD_OPEN|wx.FD_CHANGE_DIR)
1296 filename = None
1297 if dlg.ShowModal() == wx.ID_OK:
1298 filename = Path(dlg.GetPath()).absolute().as_posix()
1299 dlg.Destroy()
1301 if filename is None:
1302 return
1303 if self.mca is not None:
1304 self.mca2 = copy.deepcopy(self.mca)
1306 self.add_mca(GSEMCA_File(filename), filename=filename)
1308 def onSaveMCAFile(self, event=None, **kws):
1309 deffile = ''
1310 if getattr(self.mca, 'sourcefile', None) is not None:
1311 deffile = "%s%s" % (deffile, self.mca.sourcefile)
1312 elif getattr(self.mca, 'filename', None) is not None:
1313 deffile = "%s%s" % (deffile, self.mca.filename)
1314 if getattr(self.mca, 'areaname', None) is not None:
1315 deffile = "%s_%s" % (deffile, self.mca.areaname)
1316 if deffile == '':
1317 deffile ='test'
1318 if not deffile.endswith('.mca'):
1319 deffile = deffile + '.mca'
1321 deffile = fix_filename(Path(deffile).name)
1322 outfile = FileSave(self, "Save MCA File",
1323 default_file=deffile,
1324 wildcard=FILE_WILDCARDS)
1325 if outfile is not None:
1326 self.mca.save_mcafile(outfile)
1329 def onSaveColumnFile(self, event=None, **kws):
1330 deffile = ''
1331 if getattr(self.mca, 'sourcefile', None) is not None:
1332 deffile = "%s%s" % (deffile, self.mca.sourcefile)
1333 elif getattr(self.mca, 'filename', None) is not None:
1334 deffile = "%s%s" % (deffile, self.mca.filename)
1336 if getattr(self.mca, 'areaname', None) is not None:
1337 deffile = "%s_%s" % (deffile, self.mca.areaname)
1338 if deffile == '':
1339 deffile ='test'
1340 if not deffile.endswith('.dat'):
1341 deffile = deffile + '.dat'
1343 deffile = fix_filename(Path(deffile).name)
1344 ASCII_WILDCARDS = "Data File (*.dat)|*.dat|All files (*.*)|*.*"
1345 outfile = FileSave(self, "Save ASCII File for MCA Data",
1346 default_file=deffile,
1347 wildcard=ASCII_WILDCARDS)
1348 if outfile is not None:
1349 self.mca.save_ascii(outfile)
1351 def onCalibrateEnergy(self, event=None, **kws):
1352 try:
1353 self.win_calib.Raise()
1354 except:
1355 self.win_calib = XRFCalibrationFrame(self, mca=self.mca,
1356 callback=self.onCalibrationChange)
1358 def onCalibrationChange(self, mca):
1359 """update whenn mca changed calibration"""
1360 self.plotmca(mca)
1362 def onFitSpectrum(self, event=None, **kws):
1363 try:
1364 self.win_fit.Raise()
1365 except:
1366 self.win_fit = FitSpectraFrame(self)
1368 def write_message(self, s, panel=0):
1369 """write a message to the Status Bar"""
1370 self.SetStatusText(s, panel)
1372 def showInspectionTool(self, event=None):
1373 app = wx.GetApp()
1374 app.ShowInspectionTool()
1376 def onAbout(self, event=None):
1377 dlg = wx.MessageDialog(self,
1378 """XRF Spectral Viewer
1379 Matt Newville <newville @ cars.uchicago.edu>
1380 """,
1381 "About XRF Viewer",
1382 wx.OK | wx.ICON_INFORMATION)
1383 dlg.ShowModal()
1384 dlg.Destroy()
1386 def onReadFile(self, event=None):
1387 dlg = wx.FileDialog(self, message="Read MCA File",
1388 defaultDir=get_cwd(),
1389 wildcard=FILE_WILDCARDS,
1390 style=wx.FD_OPEN)
1391 path, re1ad = None, False
1392 if dlg.ShowModal() == wx.ID_OK:
1393 read = True
1394 path = dlg.GetPath().replace('\\', '/')
1395 if path in self.filemap:
1396 read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path,
1397 'Re-read file?', style=wx.YES_NO))
1398 dlg.Destroy()
1399 return path
1402class XRFApp(LarchWxApp):
1403 def __init__(self, filename=None, **kws):
1404 self.filename = filename
1405 LarchWxApp.__init__(self, **kws)
1407 def createApp(self):
1408 frame = XRFDisplayFrame(filename=self.filename)
1409 frame.Show()
1410 self.SetTopWindow(frame)
1411 return True