Coverage for /Users/Newville/Codes/xraylarch/larch/epics/xrfcontrol.py: 14%
528 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"""
3Epics XRF Display App
4"""
6import sys
7import os
9import time
10import copy
11from functools import partial
13import wx
14import wx.lib.mixins.inspection
15import wx.lib.scrolledpanel as scrolled
16import wx.dataview as dv
18import numpy as np
19import matplotlib
21from wxmplot import PlotPanel
22from wxutils import (SimpleText, EditableListBox, Font, FloatCtrl,
23 pack, Popup, Button, get_icon, Check, MenuItem,
24 Choice, FileOpen, FileSave, fix_filename, HLine,
25 GridPanel, CEN, LEFT, RIGHT)
27import larch
28from larch.site_config import icondir
29from larch.wxlib import PeriodicTablePanel, LarchWxApp
30from larch.wxlib.xrfdisplay import (XRFDisplayFrame, XRFCalibrationFrame,
31 FILE_WILDCARDS)
32from larch.utils import get_cwd
34ROI_WILDCARD = 'Data files (*.dat)|*.dat|ROI files (*.roi)|*.roi|All files (*.*)|*.*'
35try:
36 from epics import caget
37 from .xrf_detectors import Epics_MultiXMAP, Epics_Xspress3
38except:
39 pass
41HAS_SCANDB = False
42try:
43 from epicsscan import ScanDB
44 HAS_SCANDB = True
45except:
46 pass
48class DetectorSelectDialog(wx.Dialog):
49 """Connect to an Epics MCA detector
50 Can be either XIA xMAP or Quantum XSPress3
51 """
52 msg = '''Select XIA xMAP or Quantum XSPress3 MultiElement MCA detector'''
53 det_types = ('SXD-7', 'ME-7', 'ME-4', 'other')
54 ioc_types = ('Xspress3.1', 'xMAP', 'Xspress3.0')
55 def_prefix = '13QX7:' # SDD1:'
56 def_nelem = 4
58 def __init__(self, parent=None, prefix=None, det_type='ME-4',
59 ioc_type='Xspress3', nmca=4,
60 title='Select Epics MCA Detector'):
61 if prefix is None:
62 prefix = self.def_prefix
63 if det_type not in self.det_types:
64 det_type = self.det_types[0]
66 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title)
68 self.SetBackgroundColour((240, 240, 230))
69 self.SetFont(Font(9))
70 if parent is not None:
71 self.SetFont(parent.GetFont())
73 self.ioctype = Choice(self,size=(120, -1), choices=self.ioc_types)
74 self.ioctype.SetStringSelection(ioc_type)
76 self.dettype = Choice(self,size=(120, -1), choices=self.det_types)
77 self.dettype.SetStringSelection(det_type)
79 self.prefix = wx.TextCtrl(self, -1, prefix, size=(120, -1))
80 self.nelem = FloatCtrl(self, value=nmca, precision=0, minval=1,
81 size=(120, -1))
83 btnsizer = wx.StdDialogButtonSizer()
85 if wx.Platform != "__WXMSW__":
86 btn = wx.ContextHelpButton(self)
87 btnsizer.AddButton(btn)
89 btn = wx.Button(self, wx.ID_OK)
90 btn.SetHelpText("Use this detector")
91 btn.SetDefault()
92 btnsizer.AddButton(btn)
94 btn = wx.Button(self, wx.ID_CANCEL)
95 btnsizer.AddButton(btn)
96 btnsizer.Realize()
98 hline = wx.StaticLine(self, size=(225, 3), style=wx.LI_HORIZONTAL)
99 sty = LEFT
100 sizer = wx.GridBagSizer(5, 2)
101 def txt(label):
102 return SimpleText(self, label, size=(120, -1), style=LEFT)
104 sizer.Add(txt(' Detector Type'), (0, 0), (1, 1), sty, 2)
105 sizer.Add(txt(' Uses Xspress3?'), (1, 0), (1, 1), sty, 2)
106 sizer.Add(txt(' Epics Prefix'), (2, 0), (1, 1), sty, 2)
107 sizer.Add(txt(' # Elements'), (3, 0), (1, 1), sty, 2)
108 sizer.Add(self.dettype, (0, 1), (1, 1), sty, 2)
109 sizer.Add(self.ioctype, (1, 1), (1, 1), sty, 2)
110 sizer.Add(self.prefix, (2, 1), (1, 1), sty, 2)
111 sizer.Add(self.nelem, (3, 1), (1, 1), sty, 2)
113 sizer.Add(hline, (4, 0), (1, 2), sty, 2)
114 sizer.Add(btnsizer, (5, 0), (1, 2), sty, 2)
115 self.SetSizer(sizer)
116 sizer.Fit(self)
119class EpicsXRFDisplayFrame(XRFDisplayFrame):
120 _about = """Epics XRF Spectra Display
121 Matt Newville <newville @ cars.uchicago.edu>
122 """
123 me4_layout = ((0, 0), (1, 0), (1, 1), (0, 1))
124 main_title = 'Epics XRF Control'
126 def __init__(self, parent=None, _larch=None, prefix=None,
127 det_type='ME-4', ioc_type='Xspress3', nmca=4,
128 size=(725, 580), environ_file=None, scandb_conn=None,
129 title='Epics XRF Display', output_title='XRF', **kws):
131 self.det_type = det_type
132 self.ioc_type = ioc_type
133 self.nmca = nmca
134 self.det_fore = 1
135 self.det_back = 0
136 self.scandb = None
137 self.environ = []
138 if environ_file is not None:
139 self.read_environfile(environ_file)
140 if HAS_SCANDB and scandb_conn is not None:
141 self.ConnectScanDB(**scandb_conn)
143 self.onConnectEpics(event=None, prefix=prefix)
145 self.icon_file = os.path.join(icondir, 'ptable.ico')
147 XRFDisplayFrame.__init__(self, parent=parent, _larch=_larch,
148 title=title, size=size, **kws)
150 def read_environfile(self, filename):
151 """read environmnet file"""
152 if os.path.exists(filename):
153 textlines = []
154 try:
155 with open(filename, 'r') as fh:
156 textlines = fh.readlines()
157 except IOError:
158 return
159 self.environ = []
160 for line in textlines:
161 line = line[:-1].replace('\t', ' ')
162 pvname, desc = line.split(None, 1)
163 desc = desc.strip()
164 self.environ.append((pvname, desc))
166 def onConnectEpics(self, event=None, prefix=None, **kws):
167 if prefix is None:
168 res = self.prompt_for_detector(prefix=prefix,
169 ioc_type=self.ioc_type,
170 nmca=self.nmca)
171 self.prefix, self.det_type, self.ioc_type, self.nmca = res
172 else:
173 self.prefix = prefix
174 self.det_fore = 1
175 self.det_back = 0
176 self.clear_mcas()
177 self.connect_to_detector(prefix=self.prefix, ioc_type=self.ioc_type,
178 det_type=self.det_type, nmca=self.nmca)
180 def ConnectScanDB(self, **kws):
181 if not HAS_SCANDB:
182 return
183 self.scandb = ScanDB(**kws)
184 if self.scandb is not None:
185 basedir = self.scandb.get_info('user_folder')
186 fileroot = self.scandb.get_info('server_fileroot')
187 basedir = str(basedir)
188 fileroot = str(fileroot)
189 if basedir.startswith(fileroot):
190 basedir = basedir[len(fileroot):]
191 fullpath = os.path.join(fileroot, basedir)
192 fullpath = fullpath.replace('\\', '/').replace('//', '/')
193 curdir = get_cwd()
194 try:
195 os.chdir(fullpath)
196 except:
197 os.chdir(curdir)
198 self.scandb.connect_pvs()
200 def onSaveMCAFile(self, event=None, **kws):
201 tmp = '''
202 # print('SaveMCA File')
203 deffile = ''
204 if hasattr(self.mca, 'sourcefile'):
205 deffile = "%s%s" % (deffile, getattr(self.mca, 'sourcefile'))
206 if hasattr(self.mca, 'areaname'):
207 deffile = "%s%s" % (deffile, getattr(self.mca, 'areaname'))
208 if deffile == '':
209 deffile ='test'
210 if not deffile.endswith('.mca'):
211 deffile = deffile + '.mca'
212 '''
214 deffile = 'save.mca' # fix_filename(str(deffile))
215 outfile = FileSave(self, "Save MCA File",
216 default_file=deffile,
217 wildcard=FILE_WILDCARDS)
219 environ = []
220 if HAS_SCANDB and self.scandb is not None:
221 c, table = self.scandb.get_table('pvs')
222 pvrows = self.scandb.query(table).all()
223 for row in pvrows:
224 addr = str(row.name)
225 desc = str(row.notes)
226 val = self.scandb.pvs[addr].get(as_string=True)
227 environ.append((addr, val, desc))
229 elif len(self.environ) > 0:
230 for pvname, desc in self.environ:
231 val = caget(pvname, as_string=True)
232 environ.append((pvname, val, desc))
234 if outfile is not None:
235 self.det.save_mcafile(outfile, environ=environ)
237 def onSaveColumnFile(self, event=None, **kws):
238 print( ' EPICS-XRFDisplay onSaveColumnFile not yet implemented ')
239 pass
241 def prompt_for_detector(self, prefix=None, ioc_type='Xspress3', nmca=4):
242 dlg = DetectorSelectDialog(prefix=prefix, ioc_type=ioc_type, nmca=nmca)
243 dlg.Raise()
244 if dlg.ShowModal() == wx.ID_OK:
245 dpref = dlg.prefix.GetValue()
246 atype = dlg.ioctype.GetStringSelection()
247 dtype = dlg.dettype.GetStringSelection()
248 nmca = dlg.nelem.GetValue()
249 dlg.Destroy()
250 return dpref, dtype, atype, nmca
252 def connect_to_detector(self, prefix=None, ioc_type='Xspress3',
253 det_type=None, nmca=4):
254 self.det = None
255 ioc_type = ioc_type.lower()
256 if ioc_type.startswith('xspress3'):
257 version = 2
258 if 'old' in ioc_type:
259 version = 1
260 self.det = Epics_Xspress3(prefix=prefix, nmca=nmca, version=version)
261 self.det.connect()
262 time.sleep(0.5)
263 self.det.get_mca(mca=1)
264 self.needs_newplot=True
265 else:
266 self.det = Epics_MultiXMAP(prefix=prefix, nmca=nmca)
267 time.sleep(0.05)
269 def show_mca(self, init=False):
270 self.needs_newplot = False
271 if self.mca is None or self.needs_newplot:
272 self.mca = self.det.get_mca(mca=self.det_fore)
274 self.plotmca(self.mca, set_title=False, init=init)
275 title = "Foreground: MCA{:d}".format(self.det_fore)
276 if self.det_back > 0:
277 if self.mca2 is None:
278 self.mca2 = self.det.get_mca(mca=self.det_back)
280 c2 = self.det.get_array(mca=self.det_back)
281 e2 = self.det.get_energy(mca=self.det_back)
282 title = "{:s} Background: MCA{:d}".format(title, self.det_back)
283 try:
284 self.oplot(e2, c2)
285 except ValueError:
286 pass
288 roiname = self.get_roiname()
290 if roiname in self.wids['roilist'].GetStrings():
291 i = self.wids['roilist'].GetStrings().index(roiname)
292 self.wids['roilist'].EnsureVisible(i)
293 self.onROI(label=roiname)
294 deadtime = self.det.get_deadtime(mca=self.det_fore)
295 if deadtime is not None:
296 self.wids['deadtime'].SetLabel("%.1f" % deadtime)
297 self.SetTitle("%s: %s" % (self.main_title, title))
298 self.needs_newplot = False
300 def onSaveROIs(self, event=None, **kws):
301 dlg = wx.FileDialog(self, message="Save ROI File",
302 defaultDir=get_cwd(),
303 wildcard=ROI_WILDCARD,
304 style = wx.FD_SAVE|wx.FD_CHANGE_DIR)
306 if dlg.ShowModal() == wx.ID_OK:
307 roifile = dlg.GetPath()
309 self.det.save_rois(roifile)
311 def onRestoreROIs(self, event=None, **kws):
312 dlg = wx.FileDialog(self, message="Read ROI File",
313 defaultDir=get_cwd(),
314 wildcard=ROI_WILDCARD,
315 style = wx.FD_OPEN|wx.FD_CHANGE_DIR)
317 if dlg.ShowModal() == wx.ID_OK:
318 roifile = dlg.GetPath()
319 self.det.restore_rois(roifile)
320 self.set_roilist(mca=self.mca)
321 self.show_mca()
322 self.onSelectDet(event=None, index=0)
324 def createCustomMenus(self):
325 menu = wx.Menu()
326 MenuItem(self, menu, "Connect to Detector\tCtrl+D",
327 "Connect to MCA or XSPress3 Detector",
328 self.onConnectEpics)
329 menu.AppendSeparator()
330 self._menus.insert(1, (menu, 'Detector'))
332 def createMainPanel(self):
333 epicspanel = self.createEpicsPanel()
334 ctrlpanel = self.createControlPanel()
335 plotpanel = self.panel = self.createPlotPanel()
336 self.panel.SetName('plotpanel')
337 tx, ty = self.wids['ptable'].GetBestSize()
338 cx, cy = ctrlpanel.GetBestSize()
339 px, py = plotpanel.GetBestSize()
341 self.SetSize((950, 625))
342 self.SetMinSize((450, 350))
344 style = wx.ALIGN_LEFT|wx.EXPAND|wx.ALL
346 bsizer = wx.BoxSizer(wx.HORIZONTAL)
347 bsizer.Add(ctrlpanel, 0, style, 1)
348 bsizer.Add(plotpanel, 1, style, 1)
349 hline = wx.StaticLine(self, size=(425, 2), style=wx.LI_HORIZONTAL|style)
351 sizer = wx.BoxSizer(wx.VERTICAL)
352 sizer.Add(epicspanel, 0, style, 1)
353 sizer.Add(hline, 0, style, 1)
354 sizer.Add(bsizer, 1, style, 1)
355 pack(self, sizer)
357 try:
358 self.SetIcon(wx.Icon(self.icon_file, wx.BITMAP_TYPE_ICO))
359 except:
360 pass
361 self.set_roilist(mca=None)
363 def create_detbuttons(self, pane):
364 btnpanel = wx.Panel(pane, name='buttons')
365 btnsizer = wx.GridBagSizer(1, 1)
366 btns = {}
367 sx = 36
368 sy = int(sx/2)
369 for i in range(1, self.nmca+1):
370 b = Button(btnpanel, f'{i}', size=(sx, sx),
371 action=partial(self.onSelectDet, index=i))
372 b.SetFont(Font(9))
373 self.wids['det%i' % i] = b
374 btns[i] = b
375 dtype = self.det_type.lower().replace('-', '').replace(' ', '').replace('_', '')
376 if dtype.startswith('sxd7') and self.nmca == 7:
377 btnsizer.Add((sx, sy), (0, 0), (1, 2), wx.ALIGN_LEFT, 1)
378 btnsizer.Add(btns[6], (1, 0), (2, 2), wx.ALIGN_LEFT, 1)
379 btnsizer.Add(btns[7], (3, 0), (2, 2), wx.ALIGN_LEFT, 1)
380 btnsizer.Add((sx, sy), (5, 0), (1, 2), wx.ALIGN_LEFT, 1)
381 btnsizer.Add(btns[5], (0, 2), (2, 2), wx.ALIGN_LEFT, 1)
382 btnsizer.Add(btns[4], (2, 2), (2, 2), wx.ALIGN_LEFT, 1)
383 btnsizer.Add(btns[1], (4, 2), (2, 2), wx.ALIGN_LEFT, 1)
384 btnsizer.Add((sx, sy), (0, 4), (1, 2), wx.ALIGN_LEFT, 1)
385 btnsizer.Add(btns[3], (1, 4), (2, 2), wx.ALIGN_LEFT, 1)
386 btnsizer.Add(btns[2], (3, 4), (2, 2), wx.ALIGN_LEFT, 1)
387 btnsizer.Add((sx, sy), (5, 4), (1, 2), wx.ALIGN_LEFT, 1)
388 elif dtype.startswith('me7') and self.nmca == 7:
389 btnsizer.Add((sx, sy), (0, 0), (1, 2), wx.ALIGN_LEFT, 1)
390 btnsizer.Add(btns[7], (1, 0), (2, 2), wx.ALIGN_LEFT, 1)
391 btnsizer.Add(btns[6], (3, 0), (2, 2), wx.ALIGN_LEFT, 1)
392 btnsizer.Add((sx, sy), (5, 0), (1, 2), wx.ALIGN_LEFT, 1)
393 btnsizer.Add(btns[2], (0, 2), (2, 2), wx.ALIGN_LEFT, 1)
394 btnsizer.Add(btns[1], (2, 2), (2, 2), wx.ALIGN_LEFT, 1)
395 btnsizer.Add(btns[5], (4, 2), (2, 2), wx.ALIGN_LEFT, 1)
396 btnsizer.Add((sx, sy), (0, 4), (1, 2), wx.ALIGN_LEFT, 1)
397 btnsizer.Add(btns[3], (1, 4), (2, 2), wx.ALIGN_LEFT, 1)
398 btnsizer.Add(btns[4], (3, 4), (2, 2), wx.ALIGN_LEFT, 1)
399 btnsizer.Add((sx, sy), (5, 4), (1, 2), wx.ALIGN_LEFT, 1)
400 elif dtype.startswith('me4') and self.nmca == 4:
401 btnsizer.Add(btns[1], (0, 0), (1, 1), wx.ALIGN_LEFT, 1)
402 btnsizer.Add(btns[2], (1, 0), (1, 1), wx.ALIGN_LEFT, 1)
403 btnsizer.Add(btns[3], (1, 1), (1, 1), wx.ALIGN_LEFT, 1)
404 btnsizer.Add(btns[4], (0, 1), (1, 1), wx.ALIGN_LEFT, 1)
405 else:
406 NPERROW = 4
407 icol, irow = 0, 0
408 for nmca in range(1, self.nmca+1):
409 btnsizer.Add(btns[nmca], (irow, icol), (1, 1), wx.ALIGN_LEFT, 1)
410 icol += 1
411 if icol > NPERROW-1:
412 icol = 0
413 irow += 1
415 pack(btnpanel, btnsizer)
416 return btnpanel
418 def createEpicsPanel(self):
419 pane = wx.Panel(self, name='epics panel')
420 style = wx.ALIGN_LEFT
421 rstyle = wx.ALIGN_RIGHT
423 det_btnpanel = self.create_detbuttons(pane)
425 bkg_choices = ['None'] + ["%d" % (i+1) for i in range(self.nmca)]
427 self.wids['det_status'] = SimpleText(pane, ' ', size=(120, -1), style=style)
428 self.wids['deadtime'] = SimpleText(pane, ' ', size=(120, -1), style=style)
430 self.wids['bkg_det'] = Choice(pane, size=(100, -1), choices=bkg_choices,
431 action=self.onSelectDet)
433 self.wids['dwelltime'] = FloatCtrl(pane, value=0.0, precision=1, minval=0,
434 size=(80, -1), act_on_losefocus=True,
435 action=self.onSetDwelltime)
436 self.wids['elapsed'] = SimpleText(pane, ' ', size=(80, -1), style=style)
438 self.wids['mca_sum'] = Choice(pane, size=(100, -1),
439 choices=['Single', 'Accumulate'],
440 action=self.onMcaSumChoice,
441 default=1 )
443 b1 = Button(pane, 'Start', size=(90, -1), action=self.onStart)
444 b2 = Button(pane, 'Stop', size=(90, -1), action=self.onStop)
445 b3 = Button(pane, 'Erase', size=(90, -1), action=self.onErase)
446 b4 = Button(pane, 'Continuous', size=(90, -1), action=partial(self.onStart,
447 dtime=0.0))
449 sum_lab = SimpleText(pane, 'Accumulate Mode:', size=(150, -1))
450 bkg_lab = SimpleText(pane, 'Background MCA:', size=(150, -1))
451 pre_lab = SimpleText(pane, 'Dwell Time (s):', size=(125, -1))
452 ela_lab = SimpleText(pane, 'Elapsed Time (s):', size=(125, -1))
453 sta_lab = SimpleText(pane, 'Status :', size=(100, -1))
454 dea_lab = SimpleText(pane, '% Deadtime:', size=(100, -1))
456 psizer = wx.GridBagSizer(5, 5)
457 psizer.Add(SimpleText(pane, ' MCAs: '), (0, 0), (1, 1), style, 1)
458 psizer.Add(det_btnpanel, (0, 1), (2, 1), style, 1)
459 psizer.Add(bkg_lab, (0, 2), (1, 1), style, 1)
460 psizer.Add(self.wids['bkg_det'], (0, 3), (1, 1), style, 1)
461 psizer.Add(sum_lab, (1, 2), (1, 1), style, 1)
462 psizer.Add(self.wids['mca_sum'], (1, 3), (1, 1), style, 1)
463 psizer.Add(pre_lab, (0, 4), (1, 1), style, 1)
464 psizer.Add(ela_lab, (1, 4), (1, 1), style, 1)
465 psizer.Add(self.wids['dwelltime'], (0, 5), (1, 1), style, 1)
466 psizer.Add(self.wids['elapsed'], (1, 5), (1, 1), style, 1)
468 psizer.Add(b1, (0, 6), (1, 1), style, 1)
469 psizer.Add(b4, (0, 7), (1, 1), style, 1)
470 psizer.Add(b2, (1, 6), (1, 1), style, 1)
471 psizer.Add(b3, (1, 7), (1, 1), style, 1)
473 psizer.Add(sta_lab, (0, 8), (1, 1), style, 1)
474 psizer.Add(self.wids['det_status'], (0, 9), (1, 1), style, 1)
475 psizer.Add(dea_lab, (1, 8), (1, 1), style, 1)
476 psizer.Add(self.wids['deadtime'], (1, 9), (1, 1), style, 1)
477 pack(pane, psizer)
478 # pane.SetMinSize((500, 53))
479 self.det.connect_displays(status=self.wids['det_status'],
480 elapsed=self.wids['elapsed'])
482 wx.CallAfter(self.onSelectDet, index=1, init=True)
483 self.timer_counter = 0
484 self.mca_timer = wx.Timer(self)
485 self.Bind(wx.EVT_TIMER, self.UpdateData, self.mca_timer)
486 self.mca_timer.Start(250)
487 return pane
489 def UpdateData(self, event=None, force=False):
490 self.timer_counter += 1
491 if self.mca is None or self.needs_newplot:
492 self.show_mca()
493 # self.elapsed_real = self.det.elapsed_real
494 self.mca.real_time = self.det.elapsed_real
495 # print("Update Data ", force, self.det.needs_refresh)
497 if force or self.det.needs_refresh:
498 self.det.needs_refresh = False
499 if self.det_back > 0:
500 if self.mca2 is None:
501 self.mca2 = self.det.get_mca(mca=self.det_back)
503 counts = self.det.get_array(mca=self.det_back)
504 energy = self.det.get_energy(mca=self.det_back)
505 try:
506 self.update_mca(counts, energy=energy, is_mca2=True, draw=False)
507 except ValueError:
508 pass
510 if self.mca is None:
511 self.mca = self.det.get_mca(mca=self.det_fore)
513 dtime = self.det.get_deadtime(mca=self.det_fore)
514 if dtime is not None:
515 self.wids['deadtime'].SetLabel("%.1f" % dtime)
517 counts = self.det.get_array(mca=self.det_fore)*1.0
518 energy = self.det.get_energy(mca=self.det_fore)
519 if max(counts) < 1.0:
520 counts = 1e-4*np.ones(len(counts))
521 counts[0] = 2.0
522 self.update_mca(counts, energy=energy)
524 def ShowROIStatus(self, left, right, name='', panel=0):
525 if left > right:
526 return
527 sum = self.ydata[left:right].sum()
529 try:
530 ftime, nframes = self.det.get_frametime()
531 except:
532 ftime = self.det.frametime
533 nframes = self.det.nframes
534 self.det.elapsed_real = nframes * ftime
536 mca_counts = self.det.mcas[self.det_fore-1].get('VAL')
537 sum = mca_counts[left:right].sum()
538 # print("ROI STATUS ", name, ftime, nframes, sum, cps, mca_counts.sum(), mca_counts)
539 if name in (None, ''):
540 name = 'Selected'
541 else:
542 for roi in self.det.mcas[self.det_fore-1].rois:
543 if name.lower() == roi.name.lower():
544 try:
545 sum = roi.sum
546 except:
547 pass
548 cps = sum/ftime
549 if cps < 0: cps = 0
550 # print("ROI STATUS ", name, _counts, cps)
551 fmt = " {:s}: Cts={:10,.0f} :{:10,.1f} Hz"
552 self.write_message(fmt.format(name, sum, cps), panel=panel)
554 def onSelectDet(self, event=None, index=0, init=False, **kws):
555 if index > 0:
556 self.det_fore = index
557 self.det_back = self.wids['bkg_det'].GetSelection()
558 if self.det_fore == self.det_back:
559 self.det_back = 0
561 for i in range(1, self.nmca+1):
562 dname = 'det%i' % i
563 bcol = (210, 210, 210)
564 fcol = (0, 0, 0)
565 if i == self.det_fore:
566 fcol = (200, 20, 20)
567 bcol = (250, 250, 250)
568 self.wids[dname].SetBackgroundColour(bcol)
569 self.wids[dname].SetForegroundColour(fcol)
570 self.clear_mcas()
571 self.show_mca(init=init)
572 self.Refresh()
574 def swap_mcas(self, event=None):
575 if self.mca2 is None:
576 return
577 self.mca, self.mca2 = self.mca2, self.mca
578 fore, back = self.det_fore, self.det_back
579 self.wids['bkg_det'].SetSelection(fore)
580 self.onSelectDet(index=back)
582 def onMcaSumChoice(self, event=None):
583 wid = self.wids['mca_sum']
584 self.det.set_usesum('accum' in wid.GetStringSelection().lower())
586 def onSetDwelltime(self, event=None, **kws):
587 if 'dwelltime' in self.wids:
588 self.det.set_dwelltime(dtime=self.wids['dwelltime'].GetValue())
590 def clear_mcas(self):
591 self.mca = self.mca2 = None
592 self.x2data = self.y2data = None
593 self.needs_newplot = True
595 def onStart(self, event=None, dtime=None, **kws):
596 if dtime is not None:
597 self.wids['dwelltime'].SetValue("%.1f" % dtime)
598 self.det.set_dwelltime(dtime=dtime)
599 else:
600 self.det.set_dwelltime(dtime=self.wids['dwelltime'].GetValue())
601 self.det.start()
603 def onStop(self, event=None, **kws):
604 self.det.stop()
605 self.det.needs_refresh = True
606 time.sleep(0.05)
607 self.UpdateData(event=None, force=True)
609 def onErase(self, event=None, **kws):
610 self.needs_newplot = True
611 self.det.erase()
613 def onDelROI(self, event=None):
614 roiname = self.get_roiname()
615 errmsg = None
616 t0 = time.time()
617 if self.roilist_sel is None:
618 errmsg = 'No ROI selected to delete.'
619 if errmsg is not None:
620 return Popup(self, errmsg, 'Cannot Delete ROI')
622 self.det.del_roi(roiname)
623 XRFDisplayFrame.onDelROI(self)
626 def onNewROI(self, event=None):
627 roiname = self.get_roiname()
628 errmsg = None
629 if self.xmarker_left is None or self.xmarker_right is None:
630 errmsg = 'Must select right and left markers to define ROI'
631 elif roiname in self.wids['roilist'].GetStrings():
632 errmsg = '%s is already in ROI list - use a unique name.' % roiname
633 if errmsg is not None:
634 return Popup(self, errmsg, 'Cannot Define ROI')
636 confirmed = XRFDisplayFrame.onNewROI(self)
637 if confirmed:
638 self.det.add_roi(roiname, lo=self.xmarker_left,
639 hi=self.xmarker_right)
641 def onRenameROI(self, event=None):
642 roiname = self.get_roiname()
643 errmsg = None
644 if roiname in self.wids['roilist'].GetStrings():
645 errmsg = '%s is already in ROI list - use a unique name.' % roiname
646 elif self.roilist_sel is None:
647 errmsg = 'No ROI selected to rename.'
648 if errmsg is not None:
649 return Popup(self, errmsg, 'Cannot Rename ROI')
651 if self.roilist_sel < len(self.det.mcas[0].rois):
652 self.det.rename_roi(self.roilist_sel, roiname)
653 names = self.wids['roilist'].GetStrings()
654 names[self.roilist_sel] = roiname
655 self.wids['roilist'].Clear()
656 for sname in names:
657 self.wids['roilist'].Append(sname)
658 self.wids['roilist'].SetSelection(self.roilist_sel)
660 def onCalibrateEnergy(self, event=None, **kws):
661 try:
662 self.win_calib.Raise()
663 except:
664 self.win_calib = XRFCalibrationFrame(self, mca=self.mca,
665 larch=self.larch,
666 callback=self.onSetCalib)
668 def onSetCalib(self, offset, slope, mca=None):
669 print('XRFControl Set Energy Calibratione' , offset, slope, mca)
671 def onClose(self, event=None):
672 self.onStop()
673 XRFDisplayFrame.onClose(self)
675 def onExit(self, event=None):
676 self.onStop()
677 XRFDisplayFrame.onExit(self)
679class EpicsXRFApp(LarchWxApp):
680 def __init__(self, _larch=None, prefix=None,
681 det_type='ME-4', ioc_type='Xspress3', nmca=4,
682 size=(725, 580), environ_file=None, scandb_conn=None,
683 title='Epics XRF Display', output_title='XRF', **kws):
684 self.prefix = prefix
685 self.det_type = det_type
686 self.ioc_type = ioc_type
687 self.nmca = nmca
688 self.size = size
689 self.environ_file = environ_file
690 self.scandb_conn = scandb_conn
691 self.title = title
692 self.output_title = output_title
693 LarchWxApp.__init__(self, _larch=_larch, **kws)
695 def createApp(self):
696 frame = EpicsXRFDisplayFrame(prefix=self.prefix,
697 det_type=self.det_type,
698 ioc_type=self.ioc_type,
699 nmca=self.nmca, size=self.size,
700 environ_file=self.environ_file,
701 scandb_conn=self.scandb_conn,
702 title=self.title,
703 output_title=self.output_title,
704 _larch=self._larch)
705 frame.Show()
706 self.SetTopWindow(frame)
707 return True
709if __name__ == "__main__":
710 EpicsXRFApp().MainLoop()