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