Coverage for larch/epics/xrfcontrol.py: 14%

490 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/env python 

2""" 

3Epics XRF Display App 

4""" 

5 

6import sys 

7import os 

8 

9import time 

10import copy 

11from functools import partial 

12 

13import wx 

14import wx.lib.mixins.inspection 

15import wx.lib.scrolledpanel as scrolled 

16import wx.dataview as dv 

17 

18import numpy as np 

19import matplotlib 

20 

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) 

26 

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 

33 

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 

40 

41class DetectorSelectDialog(wx.Dialog): 

42 """Connect to an Epics MCA detector 

43 Can be either XIA xMAP or Quantum XSPress3 

44 """ 

45 msg = '''Select XIA xMAP or Quantum XSPress3 MultiElement MCA detector''' 

46 det_types = ('SXD-7', 'ME-7', 'ME-4', 'other') 

47 ioc_types = ('Xspress3.1', 'xMAP', 'Xspress3.0') 

48 def_prefix = '13QX7:' # SDD1:' 

49 def_nelem = 4 

50 

51 def __init__(self, parent=None, prefix=None, det_type='ME-4', 

52 ioc_type='Xspress3', nmca=4, 

53 title='Select Epics MCA Detector'): 

54 if prefix is None: 

55 prefix = self.def_prefix 

56 if det_type not in self.det_types: 

57 det_type = self.det_types[0] 

58 

59 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title) 

60 

61 self.SetBackgroundColour((240, 240, 230)) 

62 self.SetFont(Font(9)) 

63 if parent is not None: 

64 self.SetFont(parent.GetFont()) 

65 

66 self.ioctype = Choice(self,size=(120, -1), choices=self.ioc_types) 

67 self.ioctype.SetStringSelection(ioc_type) 

68 

69 self.dettype = Choice(self,size=(120, -1), choices=self.det_types) 

70 self.dettype.SetStringSelection(det_type) 

71 

72 self.prefix = wx.TextCtrl(self, -1, prefix, size=(120, -1)) 

73 self.nelem = FloatCtrl(self, value=nmca, precision=0, minval=1, 

74 size=(120, -1)) 

75 

76 btnsizer = wx.StdDialogButtonSizer() 

77 

78 if wx.Platform != "__WXMSW__": 

79 btn = wx.ContextHelpButton(self) 

80 btnsizer.AddButton(btn) 

81 

82 btn = wx.Button(self, wx.ID_OK) 

83 btn.SetHelpText("Use this detector") 

84 btn.SetDefault() 

85 btnsizer.AddButton(btn) 

86 

87 btn = wx.Button(self, wx.ID_CANCEL) 

88 btnsizer.AddButton(btn) 

89 btnsizer.Realize() 

90 

91 hline = wx.StaticLine(self, size=(225, 3), style=wx.LI_HORIZONTAL) 

92 sty = LEFT 

93 sizer = wx.GridBagSizer(5, 2) 

94 def txt(label): 

95 return SimpleText(self, label, size=(120, -1), style=LEFT) 

96 

97 sizer.Add(txt(' Detector Type'), (0, 0), (1, 1), sty, 2) 

98 sizer.Add(txt(' Uses Xspress3?'), (1, 0), (1, 1), sty, 2) 

99 sizer.Add(txt(' Epics Prefix'), (2, 0), (1, 1), sty, 2) 

100 sizer.Add(txt(' # Elements'), (3, 0), (1, 1), sty, 2) 

101 sizer.Add(self.dettype, (0, 1), (1, 1), sty, 2) 

102 sizer.Add(self.ioctype, (1, 1), (1, 1), sty, 2) 

103 sizer.Add(self.prefix, (2, 1), (1, 1), sty, 2) 

104 sizer.Add(self.nelem, (3, 1), (1, 1), sty, 2) 

105 

106 sizer.Add(hline, (4, 0), (1, 2), sty, 2) 

107 sizer.Add(btnsizer, (5, 0), (1, 2), sty, 2) 

108 self.SetSizer(sizer) 

109 sizer.Fit(self) 

110 

111 

112class EpicsXRFDisplayFrame(XRFDisplayFrame): 

113 _about = """Epics XRF Spectra Display 

114 Matt Newville <newville @ cars.uchicago.edu> 

115 """ 

116 me4_layout = ((0, 0), (1, 0), (1, 1), (0, 1)) 

117 main_title = 'Epics XRF Control' 

118 

119 def __init__(self, parent=None, _larch=None, prefix=None, 

120 det_type='ME-4', ioc_type='Xspress3', nmca=4, 

121 size=(725, 580), environ_file=None, 

122 title='Epics XRF Display', output_title='XRF', **kws): 

123 

124 self.det_type = det_type 

125 self.ioc_type = ioc_type 

126 self.nmca = nmca 

127 self.det_fore = 1 

128 self.det_back = 0 

129 self.environ = [] 

130 if environ_file is not None: 

131 self.read_environfile(environ_file) 

132 

133 self.onConnectEpics(event=None, prefix=prefix) 

134 

135 self.icon_file = os.path.join(icondir, 'ptable.ico') 

136 

137 XRFDisplayFrame.__init__(self, parent=parent, _larch=_larch, 

138 title=title, size=size, **kws) 

139 

140 def read_environfile(self, filename): 

141 """read environmnet file""" 

142 if os.path.exists(filename): 

143 textlines = [] 

144 try: 

145 with open(filename, 'r') as fh: 

146 textlines = fh.readlines() 

147 except IOError: 

148 return 

149 self.environ = [] 

150 for line in textlines: 

151 line = line[:-1].replace('\t', ' ') 

152 pvname, desc = line.split(None, 1) 

153 desc = desc.strip() 

154 self.environ.append((pvname, desc)) 

155 

156 def onConnectEpics(self, event=None, prefix=None, **kws): 

157 if prefix is None: 

158 res = self.prompt_for_detector(prefix=prefix, 

159 ioc_type=self.ioc_type, 

160 nmca=self.nmca) 

161 self.prefix, self.det_type, self.ioc_type, self.nmca = res 

162 else: 

163 self.prefix = prefix 

164 self.det_fore = 1 

165 self.det_back = 0 

166 self.clear_mcas() 

167 self.connect_to_detector(prefix=self.prefix, ioc_type=self.ioc_type, 

168 det_type=self.det_type, nmca=self.nmca) 

169 

170 def onSaveMCAFile(self, event=None, **kws): 

171 tmp = ''' 

172 # print('SaveMCA File') 

173 deffile = '' 

174 if hasattr(self.mca, 'sourcefile'): 

175 deffile = "%s%s" % (deffile, getattr(self.mca, 'sourcefile')) 

176 if hasattr(self.mca, 'areaname'): 

177 deffile = "%s%s" % (deffile, getattr(self.mca, 'areaname')) 

178 if deffile == '': 

179 deffile ='test' 

180 if not deffile.endswith('.mca'): 

181 deffile = deffile + '.mca' 

182 ''' 

183 

184 deffile = 'save.mca' # fix_filename(str(deffile)) 

185 outfile = FileSave(self, "Save MCA File", 

186 default_file=deffile, 

187 wildcard=FILE_WILDCARDS) 

188 

189 env = [] 

190 if len(self.environ) > 0: 

191 for pvname, desc in self.environ: 

192 val = caget(pvname, as_string=True) 

193 env.append((pvname, val, desc)) 

194 

195 if outfile is not None: 

196 self.det.save_mcafile(outfile, environ=env) 

197 

198 def onSaveColumnFile(self, event=None, **kws): 

199 print( ' EPICS-XRFDisplay onSaveColumnFile not yet implemented ') 

200 pass 

201 

202 def prompt_for_detector(self, prefix=None, ioc_type='Xspress3', nmca=4): 

203 dlg = DetectorSelectDialog(prefix=prefix, ioc_type=ioc_type, nmca=nmca) 

204 dlg.Raise() 

205 if dlg.ShowModal() == wx.ID_OK: 

206 dpref = dlg.prefix.GetValue() 

207 atype = dlg.ioctype.GetStringSelection() 

208 dtype = dlg.dettype.GetStringSelection() 

209 nmca = dlg.nelem.GetValue() 

210 dlg.Destroy() 

211 return dpref, dtype, atype, nmca 

212 

213 def connect_to_detector(self, prefix=None, ioc_type='Xspress3', 

214 det_type=None, nmca=4): 

215 self.det = None 

216 ioc_type = ioc_type.lower() 

217 if ioc_type.startswith('xspress3'): 

218 version = 2 

219 if 'old' in ioc_type: 

220 version = 1 

221 self.det = Epics_Xspress3(prefix=prefix, nmca=nmca, version=version) 

222 self.det.connect() 

223 time.sleep(0.5) 

224 self.det.get_mca(mca=1) 

225 self.needs_newplot=True 

226 else: 

227 self.det = Epics_MultiXMAP(prefix=prefix, nmca=nmca) 

228 time.sleep(0.05) 

229 

230 def show_mca(self, init=False): 

231 self.needs_newplot = False 

232 if self.mca is None or self.needs_newplot: 

233 self.mca = self.det.get_mca(mca=self.det_fore) 

234 

235 self.plotmca(self.mca, set_title=False, init=init) 

236 title = "Foreground: MCA{:d}".format(self.det_fore) 

237 if self.det_back > 0: 

238 if self.mca2 is None: 

239 self.mca2 = self.det.get_mca(mca=self.det_back) 

240 

241 c2 = self.det.get_array(mca=self.det_back) 

242 e2 = self.det.get_energy(mca=self.det_back) 

243 title = "{:s} Background: MCA{:d}".format(title, self.det_back) 

244 try: 

245 self.oplot(e2, c2) 

246 except ValueError: 

247 pass 

248 

249 roiname = self.get_roiname() 

250 

251 if roiname in self.wids['roilist'].GetStrings(): 

252 i = self.wids['roilist'].GetStrings().index(roiname) 

253 self.wids['roilist'].EnsureVisible(i) 

254 self.onROI(label=roiname) 

255 deadtime = self.det.get_deadtime(mca=self.det_fore) 

256 if deadtime is not None: 

257 self.wids['deadtime'].SetLabel("%.1f" % deadtime) 

258 self.SetTitle("%s: %s" % (self.main_title, title)) 

259 self.needs_newplot = False 

260 

261 def onSaveROIs(self, event=None, **kws): 

262 dlg = wx.FileDialog(self, message="Save ROI File", 

263 defaultDir=get_cwd(), 

264 wildcard=ROI_WILDCARD, 

265 style = wx.FD_SAVE|wx.FD_CHANGE_DIR) 

266 

267 if dlg.ShowModal() == wx.ID_OK: 

268 roifile = dlg.GetPath() 

269 

270 self.det.save_rois(roifile) 

271 

272 def onRestoreROIs(self, event=None, **kws): 

273 dlg = wx.FileDialog(self, message="Read ROI File", 

274 defaultDir=get_cwd(), 

275 wildcard=ROI_WILDCARD, 

276 style = wx.FD_OPEN|wx.FD_CHANGE_DIR) 

277 

278 if dlg.ShowModal() == wx.ID_OK: 

279 roifile = dlg.GetPath() 

280 self.det.restore_rois(roifile) 

281 self.set_roilist(mca=self.mca) 

282 self.show_mca() 

283 self.onSelectDet(event=None, index=0) 

284 

285 def createCustomMenus(self): 

286 menu = wx.Menu() 

287 MenuItem(self, menu, "Connect to Detector\tCtrl+D", 

288 "Connect to MCA or XSPress3 Detector", 

289 self.onConnectEpics) 

290 menu.AppendSeparator() 

291 self._menus.insert(1, (menu, 'Detector')) 

292 

293 def createMainPanel(self): 

294 epicspanel = self.createEpicsPanel() 

295 ctrlpanel = self.createControlPanel() 

296 plotpanel = self.panel = self.createPlotPanel() 

297 self.panel.SetName('plotpanel') 

298 tx, ty = self.wids['ptable'].GetBestSize() 

299 cx, cy = ctrlpanel.GetBestSize() 

300 px, py = plotpanel.GetBestSize() 

301 

302 self.SetSize((950, 625)) 

303 self.SetMinSize((450, 350)) 

304 

305 style = wx.ALIGN_LEFT|wx.EXPAND|wx.ALL 

306 

307 bsizer = wx.BoxSizer(wx.HORIZONTAL) 

308 bsizer.Add(ctrlpanel, 0, style, 1) 

309 bsizer.Add(plotpanel, 1, style, 1) 

310 hline = wx.StaticLine(self, size=(425, 2), style=wx.LI_HORIZONTAL|style) 

311 

312 sizer = wx.BoxSizer(wx.VERTICAL) 

313 sizer.Add(epicspanel, 0, style, 1) 

314 sizer.Add(hline, 0, style, 1) 

315 sizer.Add(bsizer, 1, style, 1) 

316 pack(self, sizer) 

317 

318 try: 

319 self.SetIcon(wx.Icon(self.icon_file, wx.BITMAP_TYPE_ICO)) 

320 except: 

321 pass 

322 self.set_roilist(mca=None) 

323 

324 def create_detbuttons(self, pane): 

325 btnpanel = wx.Panel(pane, name='buttons') 

326 btnsizer = wx.GridBagSizer(1, 1) 

327 btns = {} 

328 sx = 36 

329 sy = int(sx/2) 

330 for i in range(1, self.nmca+1): 

331 b = Button(btnpanel, f'{i}', size=(sx, sx), 

332 action=partial(self.onSelectDet, index=i)) 

333 b.SetFont(Font(9)) 

334 self.wids['det%i' % i] = b 

335 btns[i] = b 

336 dtype = self.det_type.lower().replace('-', '').replace(' ', '').replace('_', '') 

337 if dtype.startswith('sxd7') and self.nmca == 7: 

338 btnsizer.Add((sx, sy), (0, 0), (1, 2), wx.ALIGN_LEFT, 1) 

339 btnsizer.Add(btns[6], (1, 0), (2, 2), wx.ALIGN_LEFT, 1) 

340 btnsizer.Add(btns[7], (3, 0), (2, 2), wx.ALIGN_LEFT, 1) 

341 btnsizer.Add((sx, sy), (5, 0), (1, 2), wx.ALIGN_LEFT, 1) 

342 btnsizer.Add(btns[5], (0, 2), (2, 2), wx.ALIGN_LEFT, 1) 

343 btnsizer.Add(btns[4], (2, 2), (2, 2), wx.ALIGN_LEFT, 1) 

344 btnsizer.Add(btns[1], (4, 2), (2, 2), wx.ALIGN_LEFT, 1) 

345 btnsizer.Add((sx, sy), (0, 4), (1, 2), wx.ALIGN_LEFT, 1) 

346 btnsizer.Add(btns[3], (1, 4), (2, 2), wx.ALIGN_LEFT, 1) 

347 btnsizer.Add(btns[2], (3, 4), (2, 2), wx.ALIGN_LEFT, 1) 

348 btnsizer.Add((sx, sy), (5, 4), (1, 2), wx.ALIGN_LEFT, 1) 

349 elif dtype.startswith('me7') and self.nmca == 7: 

350 btnsizer.Add((sx, sy), (0, 0), (1, 2), wx.ALIGN_LEFT, 1) 

351 btnsizer.Add(btns[7], (1, 0), (2, 2), wx.ALIGN_LEFT, 1) 

352 btnsizer.Add(btns[6], (3, 0), (2, 2), wx.ALIGN_LEFT, 1) 

353 btnsizer.Add((sx, sy), (5, 0), (1, 2), wx.ALIGN_LEFT, 1) 

354 btnsizer.Add(btns[2], (0, 2), (2, 2), wx.ALIGN_LEFT, 1) 

355 btnsizer.Add(btns[1], (2, 2), (2, 2), wx.ALIGN_LEFT, 1) 

356 btnsizer.Add(btns[5], (4, 2), (2, 2), wx.ALIGN_LEFT, 1) 

357 btnsizer.Add((sx, sy), (0, 4), (1, 2), wx.ALIGN_LEFT, 1) 

358 btnsizer.Add(btns[3], (1, 4), (2, 2), wx.ALIGN_LEFT, 1) 

359 btnsizer.Add(btns[4], (3, 4), (2, 2), wx.ALIGN_LEFT, 1) 

360 btnsizer.Add((sx, sy), (5, 4), (1, 2), wx.ALIGN_LEFT, 1) 

361 elif dtype.startswith('me4') and self.nmca == 4: 

362 btnsizer.Add(btns[1], (0, 0), (1, 1), wx.ALIGN_LEFT, 1) 

363 btnsizer.Add(btns[2], (1, 0), (1, 1), wx.ALIGN_LEFT, 1) 

364 btnsizer.Add(btns[3], (1, 1), (1, 1), wx.ALIGN_LEFT, 1) 

365 btnsizer.Add(btns[4], (0, 1), (1, 1), wx.ALIGN_LEFT, 1) 

366 else: 

367 NPERROW = 4 

368 icol, irow = 0, 0 

369 for nmca in range(1, self.nmca+1): 

370 btnsizer.Add(btns[nmca], (irow, icol), (1, 1), wx.ALIGN_LEFT, 1) 

371 icol += 1 

372 if icol > NPERROW-1: 

373 icol = 0 

374 irow += 1 

375 

376 pack(btnpanel, btnsizer) 

377 return btnpanel 

378 

379 def createEpicsPanel(self): 

380 pane = wx.Panel(self, name='epics panel') 

381 style = wx.ALIGN_LEFT 

382 rstyle = wx.ALIGN_RIGHT 

383 

384 det_btnpanel = self.create_detbuttons(pane) 

385 

386 bkg_choices = ['None'] + ["%d" % (i+1) for i in range(self.nmca)] 

387 

388 self.wids['det_status'] = SimpleText(pane, ' ', size=(120, -1), style=style) 

389 self.wids['deadtime'] = SimpleText(pane, ' ', size=(120, -1), style=style) 

390 

391 self.wids['bkg_det'] = Choice(pane, size=(100, -1), choices=bkg_choices, 

392 action=self.onSelectDet) 

393 

394 self.wids['dwelltime'] = FloatCtrl(pane, value=0.0, precision=1, minval=0, 

395 size=(80, -1), act_on_losefocus=True, 

396 action=self.onSetDwelltime) 

397 self.wids['elapsed'] = SimpleText(pane, ' ', size=(80, -1), style=style) 

398 

399 self.wids['mca_sum'] = Choice(pane, size=(100, -1), 

400 choices=['Single', 'Accumulate'], 

401 action=self.onMcaSumChoice, 

402 default=1 ) 

403 

404 b1 = Button(pane, 'Start', size=(90, -1), action=self.onStart) 

405 b2 = Button(pane, 'Stop', size=(90, -1), action=self.onStop) 

406 b3 = Button(pane, 'Erase', size=(90, -1), action=self.onErase) 

407 b4 = Button(pane, 'Continuous', size=(90, -1), action=partial(self.onStart, 

408 dtime=0.0)) 

409 

410 sum_lab = SimpleText(pane, 'Accumulate Mode:', size=(150, -1)) 

411 bkg_lab = SimpleText(pane, 'Background MCA:', size=(150, -1)) 

412 pre_lab = SimpleText(pane, 'Dwell Time (s):', size=(125, -1)) 

413 ela_lab = SimpleText(pane, 'Elapsed Time (s):', size=(125, -1)) 

414 sta_lab = SimpleText(pane, 'Status :', size=(100, -1)) 

415 dea_lab = SimpleText(pane, '% Deadtime:', size=(100, -1)) 

416 

417 psizer = wx.GridBagSizer(5, 5) 

418 psizer.Add(SimpleText(pane, ' MCAs: '), (0, 0), (1, 1), style, 1) 

419 psizer.Add(det_btnpanel, (0, 1), (2, 1), style, 1) 

420 psizer.Add(bkg_lab, (0, 2), (1, 1), style, 1) 

421 psizer.Add(self.wids['bkg_det'], (0, 3), (1, 1), style, 1) 

422 psizer.Add(sum_lab, (1, 2), (1, 1), style, 1) 

423 psizer.Add(self.wids['mca_sum'], (1, 3), (1, 1), style, 1) 

424 psizer.Add(pre_lab, (0, 4), (1, 1), style, 1) 

425 psizer.Add(ela_lab, (1, 4), (1, 1), style, 1) 

426 psizer.Add(self.wids['dwelltime'], (0, 5), (1, 1), style, 1) 

427 psizer.Add(self.wids['elapsed'], (1, 5), (1, 1), style, 1) 

428 

429 psizer.Add(b1, (0, 6), (1, 1), style, 1) 

430 psizer.Add(b4, (0, 7), (1, 1), style, 1) 

431 psizer.Add(b2, (1, 6), (1, 1), style, 1) 

432 psizer.Add(b3, (1, 7), (1, 1), style, 1) 

433 

434 psizer.Add(sta_lab, (0, 8), (1, 1), style, 1) 

435 psizer.Add(self.wids['det_status'], (0, 9), (1, 1), style, 1) 

436 psizer.Add(dea_lab, (1, 8), (1, 1), style, 1) 

437 psizer.Add(self.wids['deadtime'], (1, 9), (1, 1), style, 1) 

438 pack(pane, psizer) 

439 # pane.SetMinSize((500, 53)) 

440 self.det.connect_displays(status=self.wids['det_status'], 

441 elapsed=self.wids['elapsed']) 

442 

443 wx.CallAfter(self.onSelectDet, index=1, init=True) 

444 self.timer_counter = 0 

445 self.mca_timer = wx.Timer(self) 

446 self.Bind(wx.EVT_TIMER, self.UpdateData, self.mca_timer) 

447 self.mca_timer.Start(250) 

448 return pane 

449 

450 def UpdateData(self, event=None, force=False): 

451 self.timer_counter += 1 

452 if self.mca is None or self.needs_newplot: 

453 self.show_mca() 

454 # self.elapsed_real = self.det.elapsed_real 

455 self.mca.real_time = self.det.elapsed_real 

456 # print("Update Data ", force, self.det.needs_refresh) 

457 

458 if force or self.det.needs_refresh: 

459 self.det.needs_refresh = False 

460 if self.det_back > 0: 

461 if self.mca2 is None: 

462 self.mca2 = self.det.get_mca(mca=self.det_back) 

463 

464 counts = self.det.get_array(mca=self.det_back) 

465 energy = self.det.get_energy(mca=self.det_back) 

466 try: 

467 self.update_mca(counts, energy=energy, is_mca2=True, draw=False) 

468 except ValueError: 

469 pass 

470 

471 if self.mca is None: 

472 self.mca = self.det.get_mca(mca=self.det_fore) 

473 

474 dtime = self.det.get_deadtime(mca=self.det_fore) 

475 if dtime is not None: 

476 self.wids['deadtime'].SetLabel("%.1f" % dtime) 

477 

478 counts = self.det.get_array(mca=self.det_fore)*1.0 

479 energy = self.det.get_energy(mca=self.det_fore) 

480 if max(counts) < 1.0: 

481 counts = 1e-4*np.ones(len(counts)) 

482 counts[0] = 2.0 

483 self.update_mca(counts, energy=energy) 

484 

485 def ShowROIStatus(self, left, right, name='', panel=0): 

486 if left > right: 

487 return 

488 sum = self.ydata[left:right].sum() 

489 

490 try: 

491 ftime, nframes = self.det.get_frametime() 

492 except: 

493 ftime = self.det.frametime 

494 nframes = self.det.nframes 

495 self.det.elapsed_real = nframes * ftime 

496 

497 mca_counts = self.det.mcas[self.det_fore-1].get('VAL') 

498 sum = mca_counts[left:right].sum() 

499 # print("ROI STATUS ", name, ftime, nframes, sum, cps, mca_counts.sum(), mca_counts) 

500 if name in (None, ''): 

501 name = 'Selected' 

502 else: 

503 for roi in self.det.mcas[self.det_fore-1].rois: 

504 if name.lower() == roi.name.lower(): 

505 try: 

506 sum = roi.sum 

507 except: 

508 pass 

509 cps = sum/ftime 

510 if cps < 0: cps = 0 

511 # print("ROI STATUS ", name, _counts, cps) 

512 fmt = " {:s}: Cts={:10,.0f} :{:10,.1f} Hz" 

513 self.write_message(fmt.format(name, sum, cps), panel=panel) 

514 

515 def onSelectDet(self, event=None, index=0, init=False, **kws): 

516 if index > 0: 

517 self.det_fore = index 

518 self.det_back = self.wids['bkg_det'].GetSelection() 

519 if self.det_fore == self.det_back: 

520 self.det_back = 0 

521 

522 for i in range(1, self.nmca+1): 

523 dname = 'det%i' % i 

524 bcol = (210, 210, 210) 

525 fcol = (0, 0, 0) 

526 if i == self.det_fore: 

527 fcol = (200, 20, 20) 

528 bcol = (250, 250, 250) 

529 self.wids[dname].SetBackgroundColour(bcol) 

530 self.wids[dname].SetForegroundColour(fcol) 

531 self.clear_mcas() 

532 self.show_mca(init=init) 

533 self.Refresh() 

534 

535 def swap_mcas(self, event=None): 

536 if self.mca2 is None: 

537 return 

538 self.mca, self.mca2 = self.mca2, self.mca 

539 fore, back = self.det_fore, self.det_back 

540 self.wids['bkg_det'].SetSelection(fore) 

541 self.onSelectDet(index=back) 

542 

543 def onMcaSumChoice(self, event=None): 

544 wid = self.wids['mca_sum'] 

545 self.det.set_usesum('accum' in wid.GetStringSelection().lower()) 

546 

547 def onSetDwelltime(self, event=None, **kws): 

548 if 'dwelltime' in self.wids: 

549 self.det.set_dwelltime(dtime=self.wids['dwelltime'].GetValue()) 

550 

551 def clear_mcas(self): 

552 self.mca = self.mca2 = None 

553 self.x2data = self.y2data = None 

554 self.needs_newplot = True 

555 

556 def onStart(self, event=None, dtime=None, **kws): 

557 if dtime is not None: 

558 self.wids['dwelltime'].SetValue("%.1f" % dtime) 

559 self.det.set_dwelltime(dtime=dtime) 

560 else: 

561 self.det.set_dwelltime(dtime=self.wids['dwelltime'].GetValue()) 

562 self.det.start() 

563 

564 def onStop(self, event=None, **kws): 

565 self.det.stop() 

566 self.det.needs_refresh = True 

567 time.sleep(0.05) 

568 self.UpdateData(event=None, force=True) 

569 

570 def onErase(self, event=None, **kws): 

571 self.needs_newplot = True 

572 self.det.erase() 

573 

574 def onDelROI(self, event=None): 

575 roiname = self.get_roiname() 

576 errmsg = None 

577 t0 = time.time() 

578 if self.roilist_sel is None: 

579 errmsg = 'No ROI selected to delete.' 

580 if errmsg is not None: 

581 return Popup(self, errmsg, 'Cannot Delete ROI') 

582 

583 self.det.del_roi(roiname) 

584 XRFDisplayFrame.onDelROI(self) 

585 

586 

587 def onNewROI(self, event=None): 

588 roiname = self.get_roiname() 

589 errmsg = None 

590 if self.xmarker_left is None or self.xmarker_right is None: 

591 errmsg = 'Must select right and left markers to define ROI' 

592 elif roiname in self.wids['roilist'].GetStrings(): 

593 errmsg = '%s is already in ROI list - use a unique name.' % roiname 

594 if errmsg is not None: 

595 return Popup(self, errmsg, 'Cannot Define ROI') 

596 

597 confirmed = XRFDisplayFrame.onNewROI(self) 

598 if confirmed: 

599 self.det.add_roi(roiname, lo=self.xmarker_left, 

600 hi=self.xmarker_right) 

601 

602 def onRenameROI(self, event=None): 

603 roiname = self.get_roiname() 

604 errmsg = None 

605 if roiname in self.wids['roilist'].GetStrings(): 

606 errmsg = '%s is already in ROI list - use a unique name.' % roiname 

607 elif self.roilist_sel is None: 

608 errmsg = 'No ROI selected to rename.' 

609 if errmsg is not None: 

610 return Popup(self, errmsg, 'Cannot Rename ROI') 

611 

612 if self.roilist_sel < len(self.det.mcas[0].rois): 

613 self.det.rename_roi(self.roilist_sel, roiname) 

614 names = self.wids['roilist'].GetStrings() 

615 names[self.roilist_sel] = roiname 

616 self.wids['roilist'].Clear() 

617 for sname in names: 

618 self.wids['roilist'].Append(sname) 

619 self.wids['roilist'].SetSelection(self.roilist_sel) 

620 

621 def onCalibrateEnergy(self, event=None, **kws): 

622 try: 

623 self.win_calib.Raise() 

624 except: 

625 self.win_calib = XRFCalibrationFrame(self, mca=self.mca, 

626 larch=self.larch, 

627 callback=self.onSetCalib) 

628 

629 def onSetCalib(self, offset, slope, mca=None): 

630 print('XRFControl Set Energy Calibratione' , offset, slope, mca) 

631 

632 def onClose(self, event=None): 

633 self.onStop() 

634 XRFDisplayFrame.onClose(self) 

635 

636 def onExit(self, event=None): 

637 self.onStop() 

638 XRFDisplayFrame.onExit(self) 

639 

640class EpicsXRFApp(LarchWxApp): 

641 def __init__(self, _larch=None, prefix=None, 

642 det_type='ME-4', ioc_type='Xspress3', nmca=4, 

643 size=(725, 580), environ_file=None, 

644 title='Epics XRF Display', output_title='XRF', **kws): 

645 self.prefix = prefix 

646 self.det_type = det_type 

647 self.ioc_type = ioc_type 

648 self.nmca = nmca 

649 self.size = size 

650 self.environ_file = environ_file 

651 self.title = title 

652 self.output_title = output_title 

653 LarchWxApp.__init__(self, _larch=_larch, **kws) 

654 

655 def createApp(self): 

656 frame = EpicsXRFDisplayFrame(prefix=self.prefix, 

657 det_type=self.det_type, 

658 ioc_type=self.ioc_type, 

659 nmca=self.nmca, size=self.size, 

660 environ_file=self.environ_file, 

661 title=self.title, 

662 output_title=self.output_title, 

663 _larch=self._larch) 

664 frame.Show() 

665 self.SetTopWindow(frame) 

666 return True 

667 

668if __name__ == "__main__": 

669 EpicsXRFApp().MainLoop()