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

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 

41HAS_SCANDB = False 

42try: 

43 from epicsscan import ScanDB 

44 HAS_SCANDB = True 

45except: 

46 pass 

47 

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 

57 

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] 

65 

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

67 

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

69 self.SetFont(Font(9)) 

70 if parent is not None: 

71 self.SetFont(parent.GetFont()) 

72 

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

74 self.ioctype.SetStringSelection(ioc_type) 

75 

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

77 self.dettype.SetStringSelection(det_type) 

78 

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)) 

82 

83 btnsizer = wx.StdDialogButtonSizer() 

84 

85 if wx.Platform != "__WXMSW__": 

86 btn = wx.ContextHelpButton(self) 

87 btnsizer.AddButton(btn) 

88 

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

90 btn.SetHelpText("Use this detector") 

91 btn.SetDefault() 

92 btnsizer.AddButton(btn) 

93 

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

95 btnsizer.AddButton(btn) 

96 btnsizer.Realize() 

97 

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) 

103 

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) 

112 

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) 

117 

118 

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' 

125 

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): 

130 

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) 

142 

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

144 

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

146 

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

148 title=title, size=size, **kws) 

149 

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)) 

165 

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) 

179 

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() 

199 

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 ''' 

213 

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

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

216 default_file=deffile, 

217 wildcard=FILE_WILDCARDS) 

218 

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)) 

228 

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)) 

233 

234 if outfile is not None: 

235 self.det.save_mcafile(outfile, environ=environ) 

236 

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

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

239 pass 

240 

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 

251 

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) 

268 

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) 

273 

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) 

279 

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 

287 

288 roiname = self.get_roiname() 

289 

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 

299 

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) 

305 

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

307 roifile = dlg.GetPath() 

308 

309 self.det.save_rois(roifile) 

310 

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) 

316 

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) 

323 

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')) 

331 

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() 

340 

341 self.SetSize((950, 625)) 

342 self.SetMinSize((450, 350)) 

343 

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

345 

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) 

350 

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) 

356 

357 try: 

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

359 except: 

360 pass 

361 self.set_roilist(mca=None) 

362 

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 

414 

415 pack(btnpanel, btnsizer) 

416 return btnpanel 

417 

418 def createEpicsPanel(self): 

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

420 style = wx.ALIGN_LEFT 

421 rstyle = wx.ALIGN_RIGHT 

422 

423 det_btnpanel = self.create_detbuttons(pane) 

424 

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

426 

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

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

429 

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

431 action=self.onSelectDet) 

432 

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) 

437 

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

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

440 action=self.onMcaSumChoice, 

441 default=1 ) 

442 

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)) 

448 

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)) 

455 

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) 

467 

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) 

472 

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']) 

481 

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 

488 

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) 

496 

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) 

502 

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 

509 

510 if self.mca is None: 

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

512 

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

514 if dtime is not None: 

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

516 

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) 

523 

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

525 if left > right: 

526 return 

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

528 

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 

535 

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) 

553 

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 

560 

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() 

573 

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) 

581 

582 def onMcaSumChoice(self, event=None): 

583 wid = self.wids['mca_sum'] 

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

585 

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

587 if 'dwelltime' in self.wids: 

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

589 

590 def clear_mcas(self): 

591 self.mca = self.mca2 = None 

592 self.x2data = self.y2data = None 

593 self.needs_newplot = True 

594 

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() 

602 

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) 

608 

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

610 self.needs_newplot = True 

611 self.det.erase() 

612 

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') 

621 

622 self.det.del_roi(roiname) 

623 XRFDisplayFrame.onDelROI(self) 

624 

625 

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') 

635 

636 confirmed = XRFDisplayFrame.onNewROI(self) 

637 if confirmed: 

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

639 hi=self.xmarker_right) 

640 

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') 

650 

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) 

659 

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) 

667 

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

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

670 

671 def onClose(self, event=None): 

672 self.onStop() 

673 XRFDisplayFrame.onClose(self) 

674 

675 def onExit(self, event=None): 

676 self.onStop() 

677 XRFDisplayFrame.onExit(self) 

678 

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) 

694 

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 

708 

709if __name__ == "__main__": 

710 EpicsXRFApp().MainLoop()