Coverage for larch/wxlib/xrfdisplay.py: 10%

1051 statements  

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

1#!/usr/bin/env python 

2""" 

3GUI Frame for XRF display, reading larch MCA group 

4 

5""" 

6import sys 

7import time 

8import copy 

9from functools import partial 

10from pathlib import Path 

11import wx 

12import wx.lib.mixins.inspection 

13import wx.lib.scrolledpanel as scrolled 

14import wx.dataview as dv 

15import wx.lib.colourselect as csel 

16 

17import numpy as np 

18import matplotlib 

19from matplotlib.ticker import LogFormatter, FuncFormatter 

20 

21from wxmplot import PlotPanel 

22from . import (SimpleText, EditableListBox, Font, pack, Popup, 

23 get_icon, SetTip, Button, Check, MenuItem, Choice, 

24 FileOpen, FileSave, fix_filename, HLine, GridPanel, 

25 CEN, LEFT, RIGHT, PeriodicTablePanel, 

26 FONTSIZE, FONTSIZE_FW) 

27 

28from ..math import index_of 

29from ..utils import bytes2str, get_cwd 

30from ..io import GSEMCA_File 

31from ..site_config import icondir 

32from ..interpreter import Interpreter 

33 

34from .gui_utils import LarchWxApp 

35from .larchframe import LarchFrame 

36# from .periodictable import PeriodicTablePanel 

37 

38from .xrfdisplay_utils import (XRFCalibrationFrame, ColorsFrame, 

39 XrayLinesFrame, XRFDisplayConfig, XRFGROUP, 

40 MAKE_XRFGROUP_CMD, next_mcaname) 

41 

42from .xrfdisplay_fitpeaks import FitSpectraFrame 

43 

44FILE_WILDCARDS = "MCA File (*.mca)|*.mca|All files (*.*)|*.*" 

45FILE_ALREADY_READ = """The File 

46 '%s' 

47has already been read. 

48""" 

49 

50ICON_FILE = 'ptable.ico' 

51 

52read_mcafile = "# {group:s}.{name:s} = read_gsemca('{filename:s}')" 

53 

54def txt(panel, label, size=75, colour=None, font=None, style=None): 

55 if style is None: 

56 style = wx.ALIGN_LEFT|wx.ALL|wx.GROW 

57 if colour is None: 

58 colour = wx.Colour(0, 0, 50) 

59 this = SimpleText(panel, label, size=(size, -1), 

60 colour=colour, style=style) 

61 if font is not None: this.SetFont(font) 

62 return this 

63 

64def lin(panel, len=30, wid=2, style=wx.LI_HORIZONTAL): 

65 return wx.StaticLine(panel, size=(len, wid), style=style) 

66 

67 

68class XRFDisplayFrame(wx.Frame): 

69 _about = """XRF Spectral Viewer 

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

71 """ 

72 main_title = 'XRF Display' 

73 def __init__(self, _larch=None, parent=None, filename=None, 

74 size=(725, 450), axissize=None, axisbg=None, 

75 title='XRF Display', exit_callback=None, 

76 output_title='XRF', roi_callback=None, **kws): 

77 

78 if size is None: size = (725, 450) 

79 wx.Frame.__init__(self, parent=parent, 

80 title=title, size=size, **kws) 

81 self.conf = XRFDisplayConfig() 

82 self.subframes = {} 

83 self.data = None 

84 self.title = title 

85 self.roi_callback = roi_callback 

86 self.plotframe = None 

87 self.wids = {} 

88 self.larch = _larch 

89 if isinstance(self.larch, Interpreter): # called from shell 

90 self.larch_buffer = None 

91 else: 

92 self.larch_buffer = parent 

93 if not isinstance(parent, LarchFrame): 

94 self.larch_buffer = LarchFrame(_larch=self.larch, 

95 is_standalone=False, with_raise=False) 

96 self.subframes['larchframe'] = self.larch_buffer 

97 self.larch = self.larch_buffer.larchshell 

98 self.init_larch() 

99 

100 self.exit_callback = exit_callback 

101 self.roi_patch = None 

102 self.selected_roi = None 

103 self.roilist_sel = None 

104 self.selected_elem = None 

105 self.mca = None 

106 self.mca2 = None 

107 self.xdata = np.arange(2048)*0.01 

108 self.ydata = np.ones(2048)*1.e-4 

109 self.x2data = None 

110 self.y2data = None 

111 self.rois_shown = False 

112 self.mca_index = 0 

113 self.major_markers = [] 

114 self.minor_markers = [] 

115 self.hold_markers = [] 

116 

117 self.hold_lines = None 

118 self.saved_lines = None 

119 self.energy_for_zoom = None 

120 self.xview_range = None 

121 self.show_yaxis = False 

122 self.xmarker_left = None 

123 self.xmarker_right = None 

124 

125 self.highlight_xrayline = None 

126 self.highlight_xrayline = None 

127 self.cursor_markers = [None, None] 

128 self.ylog_scale = True 

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

130 

131 self._menus = [] 

132 self.createMainPanel() 

133 self.createMenus() 

134 self.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL)) 

135 self.statusbar = self.CreateStatusBar(4) 

136 self.statusbar.SetStatusWidths([-5, -3, -3, -4]) 

137 statusbar_fields = ["XRF Display", " ", " ", " "] 

138 for i in range(len(statusbar_fields)): 

139 self.statusbar.SetStatusText(statusbar_fields[i], i) 

140 if filename is not None: 

141 if isinstance(filename, Path): 

142 filename = Path(filename).absolute().as_posix() 

143 

144 self.add_mca(GSEMCA_File(filename), filename=filename, plot=True) 

145 

146 

147 def ignoreEvent(self, event=None): 

148 pass 

149 

150 def on_cursor(self, event=None, side='left'): 

151 if event is None: 

152 return 

153 x, y = event.xdata, event.ydata 

154 if len(self.panel.fig.axes) > 1: 

155 try: 

156 x, y = self.panel.axes.transData.inverted().transform((event.x, event.y)) 

157 except: 

158 pass 

159 ix = x 

160 if self.mca is not None: 

161 try: 

162 ix = index_of(self.mca.energy, x) 

163 except TypeError: 

164 pass 

165 

166 if side == 'right': 

167 self.xmarker_right = ix 

168 elif side == 'left': 

169 self.xmarker_left = ix 

170 

171 if self.xmarker_left is not None and self.xmarker_right is not None: 

172 ix1, ix2 = self.xmarker_left, self.xmarker_right 

173 self.xmarker_left = min(ix1, ix2) 

174 self.xmarker_right = max(ix1, ix2) 

175 

176 if side == 'left' and ix is not None: 

177 self.energy_for_zoom = self.mca.energy[ix] 

178 self.update_status() 

179 self.draw() 

180 

181 def clear_lines(self, evt=None): 

182 "remove all Line Markers" 

183 for m in self.major_markers + self.minor_markers + self.hold_markers: 

184 try: 

185 m.remove() 

186 except: 

187 pass 

188 if self.highlight_xrayline is not None: 

189 try: 

190 self.highlight_xrayline.remove() 

191 except: 

192 pass 

193 

194 self.highlight_xrayline = None 

195 self.major_markers = [] 

196 self.minor_markers = [] 

197 self.hold_markers = [] 

198 self.draw() 

199 

200 def draw(self): 

201 try: 

202 self.panel.canvas.draw() 

203 except: 

204 pass 

205 

206 def clear_markers(self, evt=None): 

207 "remove all Cursor Markers" 

208 for m in self.cursor_markers: 

209 if m is not None: 

210 m.remove() 

211 self.cursor_markers = [None, None] 

212 self.xmarker_left = None 

213 self.xmarker_right = None 

214 self.draw() 

215 

216 def clear_background(self, evt=None): 

217 "remove XRF background" 

218 self.mca2 = None 

219 self.plotmca(self.mca) 

220 

221 def update_status(self): 

222 fmt = "{:s}:{:}, E={:.3f}, Cts={:,.0f}".format 

223 if (self.xmarker_left is None and 

224 self.xmarker_right is None and 

225 self.selected_roi is None): 

226 return 

227 

228 log = np.log10 

229 axes= self.panel.axes 

230 def draw_ymarker_range(idx, x, y): 

231 ymin, ymax = self.panel.axes.get_ylim() 

232 y1 = (y-ymin)/(ymax-ymin+0.0002) 

233 if y < 1.0: y = 1.0 

234 if self.ylog_scale: 

235 y1 = (log(y)-log(ymin))/(log(ymax)-log(ymin)+2.e-9) 

236 if y1 < 0.0: y1 = 0.0 

237 y2 = min(y1+0.25, y1*0.1 + 0.9) 

238 if self.cursor_markers[idx] is not None: 

239 try: 

240 self.cursor_markers[idx].remove() 

241 except: 

242 pass 

243 self.cursor_markers[idx] = axes.axvline(x, y1, y2, linewidth=2.5, 

244 color=self.conf.marker_color) 

245 

246 if self.xmarker_left is not None: 

247 ix = self.xmarker_left 

248 x, y = self.xdata[ix], self.ydata[ix] 

249 draw_ymarker_range(0, x, y) 

250 self.write_message(fmt("L", ix, x, y), panel=1) 

251 if self.xmarker_right is not None: 

252 ix = self.xmarker_right 

253 x, y = self.xdata[ix], self.ydata[ix] 

254 draw_ymarker_range(1, x, y) 

255 self.write_message(fmt("R", ix, x, y), panel=2) 

256 

257 if self.mca is None: 

258 return 

259 

260 if (self.xmarker_left is not None and 

261 self.xmarker_right is not None): 

262 self.ShowROIStatus(self.xmarker_left, 

263 self.xmarker_right, 

264 name='', panel=3) 

265 

266 if self.selected_roi is not None: 

267 roi = self.selected_roi 

268 left, right = roi.left, roi.right 

269 self.ShowROIStatus(left, right, name=roi.name, panel=0) 

270 self.ShowROIPatch(left, right) 

271 

272 def createPlotPanel(self): 

273 """mca plot window""" 

274 pan = PlotPanel(self, fontsize=7, axisbg='#FFFFFF', 

275 with_data_process=False, 

276 output_title='test.xrf', 

277 messenger=self.write_message) 

278 pan.SetSize((650, 350)) 

279 

280 pan.conf.grid_color='#E5E5C0' 

281 pan.conf.show_grid = False 

282 pan.conf.canvas.figure.set_facecolor('#FCFCFE') 

283 pan.conf.labelfont.set_size(6) 

284 pan.conf.labelfont.set_size(6) 

285 pan.onRightDown= partial(self.on_cursor, side='right') 

286 pan.add_cursor_mode('zoom', motion = self.ignoreEvent, 

287 leftup = self.ignoreEvent, 

288 leftdown = self.on_cursor, 

289 rightdown = partial(self.on_cursor, side='right')) 

290 return pan 

291 

292 def createControlPanel(self): 

293 ctrlpanel = wx.Panel(self, name='Ctrl Panel') 

294 ptable_fontsize = 9 

295 ptable = PeriodicTablePanel(ctrlpanel, onselect=self.onShowLines, 

296 tooltip_msg='Select Element for KLM Lines', 

297 fontsize=ptable_fontsize, size=(360, 180)) 

298 self.wids['ptable'] = ptable 

299 self.font_fixedwidth = wx.Font(FONTSIZE_FW, wx.MODERN, wx.NORMAL, wx.NORMAL) 

300 

301 labstyle = wx.ALIGN_LEFT|wx.EXPAND 

302 ctrlstyle = wx.ALIGN_LEFT 

303 txtstyle=wx.ALIGN_LEFT|wx.ST_NO_AUTORESIZE|wx.TE_PROCESS_ENTER 

304 Font9 = Font(9) 

305 Font10 = Font(10) 

306 Font11 = Font(11) 

307 # 

308 arrowpanel = wx.Panel(ctrlpanel) 

309 ssizer = wx.BoxSizer(wx.HORIZONTAL) 

310 for wname, dname in (('uparrow', 'up'), 

311 ('leftarrow', 'left'), 

312 ('rightarrow', 'right'), 

313 ('downarrow', 'down')): 

314 self.wids[wname] = wx.BitmapButton(arrowpanel, -1, 

315 get_icon(wname), 

316 style=wx.NO_BORDER) 

317 self.wids[wname].Bind(wx.EVT_BUTTON, 

318 partial(ptable.onKey, name=dname)) 

319 ssizer.Add(self.wids[wname], 0, wx.EXPAND|wx.ALL) 

320 

321 self.wids['holdbtn'] = wx.ToggleButton(arrowpanel, -1, 'Hold ', 

322 size=(85, -1)) 

323 self.wids['holdbtn'].Bind(wx.EVT_TOGGLEBUTTON, self.onToggleHold) 

324 self.wids['kseries'] = Check(arrowpanel, ' K ', action=self.onKLM) 

325 self.wids['lseries'] = Check(arrowpanel, ' L ', action=self.onKLM) 

326 self.wids['mseries'] = Check(arrowpanel, ' M ', action=self.onKLM) 

327 

328 ssizer.Add(self.wids['holdbtn'], 0, wx.EXPAND|wx.ALL, 2) 

329 ssizer.Add(self.wids['kseries'], 0, wx.EXPAND|wx.ALL, 0) 

330 ssizer.Add(self.wids['lseries'], 0, wx.EXPAND|wx.ALL, 0) 

331 ssizer.Add(self.wids['mseries'], 0, wx.EXPAND|wx.ALL, 0) 

332 pack(arrowpanel, ssizer) 

333 

334 # roi section... 

335 rsizer = wx.GridBagSizer(4, 6) 

336 roipanel = wx.Panel(ctrlpanel, name='ROI Panel') 

337 self.wids['roilist'] = wx.ListBox(roipanel, size=(140, 150)) 

338 self.wids['roilist'].Bind(wx.EVT_LISTBOX, self.onROI) 

339 self.wids['roilist'].SetMinSize((140, 150)) 

340 self.wids['roiname'] = wx.TextCtrl(roipanel, -1, '', size=(150, -1)) 

341 

342 # 

343 roibtns= wx.Panel(roipanel, name='ROIButtons') 

344 zsizer = wx.BoxSizer(wx.HORIZONTAL) 

345 z1 = Button(roibtns, 'Add', size=(70, 30), action=self.onNewROI) 

346 z2 = Button(roibtns, 'Delete', size=(70, 30), action=self.onConfirmDelROI) 

347 z3 = Button(roibtns, 'Rename', size=(70, 30), action=self.onRenameROI) 

348 

349 zsizer.Add(z1, 0, wx.EXPAND|wx.ALL, 0) 

350 zsizer.Add(z2, 0, wx.EXPAND|wx.ALL, 0) 

351 zsizer.Add(z3, 0, wx.EXPAND|wx.ALL, 0) 

352 pack(roibtns, zsizer) 

353 

354 rt1 = txt(roipanel, ' Channels:', size=80, font=Font10) 

355 rt2 = txt(roipanel, ' Energy:', size=80, font=Font10) 

356 rt3 = txt(roipanel, ' Cen, Wid:', size=80, font=Font10) 

357 m = '' 

358 self.wids['roi_msg1'] = txt(roipanel, m, size=135, font=Font10) 

359 self.wids['roi_msg2'] = txt(roipanel, m, size=135, font=Font10) 

360 self.wids['roi_msg3'] = txt(roipanel, m, size=135, font=Font10) 

361 

362 rsizer.Add(txt(roipanel, ' Regions of Interest:', size=125, font=Font11), 

363 (0, 0), (1, 3), labstyle) 

364 rsizer.Add(self.wids['roiname'], (1, 0), (1, 3), labstyle) 

365 rsizer.Add(roibtns, (2, 0), (1, 3), labstyle) 

366 rsizer.Add(rt1, (3, 0), (1, 1), LEFT) 

367 rsizer.Add(rt2, (4, 0), (1, 1), LEFT) 

368 rsizer.Add(rt3, (5, 0), (1, 1), LEFT) 

369 rsizer.Add(self.wids['roi_msg1'], (3, 1), (1, 2), labstyle) 

370 rsizer.Add(self.wids['roi_msg2'], (4, 1), (1, 2), labstyle) 

371 rsizer.Add(self.wids['roi_msg3'], (5, 1), (1, 2), labstyle) 

372 rsizer.Add(self.wids['roilist'], (0, 3), (6, 1), 

373 wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT) 

374 rsizer.SetHGap(1) 

375 

376 pack(roipanel, rsizer) 

377 # end roi section 

378 

379 # y scale 

380 yscalepanel = wx.Panel(ctrlpanel, name='YScalePanel') 

381 ysizer = wx.BoxSizer(wx.HORIZONTAL) 

382 ytitle = txt(yscalepanel, ' Y Axis:', font=Font10, size=80) 

383 yspace = txt(yscalepanel, ' ', font=Font10, size=20) 

384 ylog = Choice(yscalepanel, size=(80, 30), choices=['log', 'linear'], 

385 action=self.onLogLinear) 

386 yaxis = Check(yscalepanel, ' Show Y Scale ', action=self.onYAxis, 

387 default=False) 

388 self.wids['show_yaxis'] = yaxis 

389 ysizer.Add(ytitle, 0, wx.ALL, 0) 

390 ysizer.Add(ylog, 0, wx.EXPAND|wx.ALL, 0) 

391 ysizer.Add(yspace, 0, wx.EXPAND|wx.ALL, 0) 

392 ysizer.Add(yaxis, 0, wx.EXPAND|wx.ALL, 0) 

393 pack(yscalepanel, ysizer) 

394 

395 # zoom buttons 

396 zoompanel = wx.Panel(ctrlpanel, name='ZoomPanel') 

397 zsizer = wx.BoxSizer(wx.HORIZONTAL) 

398 z1 = Button(zoompanel, 'Zoom In', size=(80, 30), action=self.onZoomIn) 

399 z2 = Button(zoompanel, 'Zoom Out', size=(80, 30), action=self.onZoomOut) 

400 p1 = Button(zoompanel, 'Pan Lo', size=(75, 30), action=self.onPanLo) 

401 p2 = Button(zoompanel, 'Pan Hi', size=(75, 30), action=self.onPanHi) 

402 

403 zsizer.Add(p1, 0, wx.EXPAND|wx.ALL, 0) 

404 zsizer.Add(p2, 0, wx.EXPAND|wx.ALL, 0) 

405 zsizer.Add(z1, 0, wx.EXPAND|wx.ALL, 0) 

406 zsizer.Add(z2, 0, wx.EXPAND|wx.ALL, 0) 

407 pack(zoompanel, zsizer) 

408 

409 self.wids['xray_lines'] = None 

410 

411 dvstyle = dv.DV_SINGLE|dv.DV_VERT_RULES|dv.DV_ROW_LINES 

412 xlines = dv.DataViewListCtrl(ctrlpanel, style=dvstyle) 

413 xlines.SetFont(self.font_fixedwidth) 

414 self.wids['xray_lines'] = xlines 

415 

416 xw = (55, 105, 90, 95) 

417 xlines.AppendTextColumn('Line', width=xw[0]) 

418 xlines.AppendTextColumn('Energy(keV)', width=xw[1]) 

419 xlines.AppendTextColumn('Strength', width=xw[2]) 

420 xlines.AppendTextColumn('Levels', width=xw[3]) 

421 for col in (0, 1, 2, 3): 

422 this = xlines.Columns[col] 

423 this.Sortable = False 

424 align = RIGHT 

425 if col in (0, 3): 

426 align = wx.ALIGN_LEFT 

427 this.Alignment = this.Renderer.Alignment = align 

428 

429 xlines.SetMinSize((300, 240)) 

430 xlines.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, 

431 

432 self.onSelectXrayLine) 

433 store = xlines.GetStore() 

434 

435 # main layout 

436 # may have to adjust comparison.... 

437 

438 sizer = wx.BoxSizer(wx.VERTICAL) 

439 sizer.Add(roipanel, 0, labstyle) 

440 sizer.Add(lin(ctrlpanel, 195), 0, labstyle) 

441 sizer.Add(yscalepanel, 0, wx.EXPAND|wx.ALL) 

442 sizer.Add(zoompanel, 0, wx.EXPAND|wx.ALL) 

443 sizer.Add(lin(ctrlpanel, 195), 0, labstyle) 

444 sizer.Add(ptable, 0, wx.EXPAND|wx.ALL, 4) 

445 sizer.Add(arrowpanel, 0, labstyle) 

446 sizer.Add(lin(ctrlpanel, 195), 0, labstyle) 

447 

448 if self.wids['xray_lines'] is not None: 

449 sizer.Add(xlines, 0, wx.GROW|wx.ALL|wx.EXPAND) 

450 

451 pack(ctrlpanel, sizer) 

452 return ctrlpanel 

453 

454 def createMainPanel(self): 

455 ctrlpanel = self.createControlPanel() 

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

457 plotpanel.yformatter = self._formaty 

458 

459 tx, ty = self.wids['ptable'].GetBestVirtualSize() 

460 cx, cy = ctrlpanel.GetBestVirtualSize() 

461 px, py = plotpanel.GetBestVirtualSize() # (650, 350) 

462 self.SetSize((max(cx, tx)+px, 25+max(cy, py))) 

463 

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

465 sizer = wx.BoxSizer(wx.HORIZONTAL) 

466 sizer.Add(ctrlpanel, 0, style, 3) 

467 sizer.Add(plotpanel, 1, style, 2) 

468 

469 self.SetMinSize((450, 150)) 

470 pack(self, sizer) 

471 self.set_roilist(mca=None) 

472 

473 def init_larch(self): 

474 symtab = self.larch.symtable 

475 if not symtab.has_symbol('_sys.wx.wxapp'): 

476 symtab.set_symbol('_sys.wx.wxapp', wx.GetApp()) 

477 if not symtab.has_symbol('_sys.wx.parent'): 

478 symtab.set_symbol('_sys.wx.parent', self) 

479 

480 if not symtab.has_group(XRFGROUP): 

481 self.larch.eval(MAKE_XRFGROUP_CMD) 

482 

483 fico = Path(icondir, ICON_FILE).as_posix() 

484 try: 

485 self.SetIcon(wx.Icon(fico, wx.BITMAP_TYPE_ICO)) 

486 except: 

487 pass 

488 

489 def add_mca(self, mca, filename=None, label=None, as_mca2=False, plot=True): 

490 if as_mca2: 

491 self.mca2 = mca 

492 else: 

493 self.mca2 = self.mca 

494 self.mca = mca 

495 # print("Add MCA ", mca, as_mca2) 

496 xrfgroup = self.larch.symtable.get_group(XRFGROUP) 

497 mcaname = next_mcaname(self.larch) 

498 if filename is not None: 

499 if isinstance(filename, Path): 

500 filename = Path(filename).absolute().as_posix() 

501 self.larch.eval(read_mcafile.format(group=XRFGROUP, 

502 name=mcaname, 

503 filename=filename)) 

504 if label is None: 

505 label = filename 

506 if label is None and hasattr(mca, 'filename'): 

507 label = mca.filename 

508 if label is None: 

509 label = mcaname 

510 self.mca.label = label 

511 # push mca to mca2, save id of this mca 

512 setattr(xrfgroup, '_mca2', getattr(xrfgroup, '_mca', '')) 

513 setattr(xrfgroup, '_mca', mcaname) 

514 setattr(xrfgroup, mcaname, mca) 

515 # print("Add MCA ", xrfgroup, mcaname) 

516 if plot: 

517 self.plotmca(self.mca) 

518 if as_mca2: 

519 self.plotmca(self.mca, as_mca2=True) 

520 

521 def _getlims(self): 

522 emin, emax = self.panel.axes.get_xlim() 

523 erange = emax-emin 

524 emid = (emax+emin)/2.0 

525 if self.energy_for_zoom is not None: 

526 emid = self.energy_for_zoom 

527 dmin, dmax = emin, emax 

528 drange = erange 

529 if self.mca is not None: 

530 dmin, dmax = self.mca.energy.min(), self.mca.energy.max() 

531 return (emid, erange, dmin, dmax) 

532 

533 def _set_xview(self, e1, e2, keep_zoom=False): 

534 if not keep_zoom: 

535 self.energy_for_zoom = (e1+e2)/2.0 

536 self.panel.axes.set_xlim((e1, e2)) 

537 self.xview_range = [e1, e2] 

538 self.draw() 

539 

540 def onPanLo(self, event=None): 

541 emid, erange, dmin, dmax = self._getlims() 

542 e1 = max(dmin, emid-0.9*erange) 

543 e2 = min(dmax, e1 + erange) 

544 self._set_xview(e1, e2) 

545 

546 def onPanHi(self, event=None): 

547 emid, erange, dmin, dmax = self._getlims() 

548 e2 = min(dmax, emid+0.9*erange) 

549 e1 = max(dmin, e2-erange) 

550 self._set_xview(e1, e2) 

551 

552 def onZoomIn(self, event=None): 

553 emid, erange, dmin, dmax = self._getlims() 

554 e1 = max(dmin, emid-erange/3.0) 

555 e2 = min(dmax, emid+erange/3.0) 

556 self._set_xview(e1, e2, keep_zoom=True) 

557 

558 def onZoomOut(self, event=None): 

559 emid, erange, dmin, dmax = self._getlims() 

560 e1 = max(dmin, emid-1.25*erange) 

561 e2 = min(dmax, emid+1.25*erange) 

562 self._set_xview(e1, e2) 

563 

564 def unzoom_all(self, event=None): 

565 emid, erange, dmin, dmax = self._getlims() 

566 self._set_xview(dmin, dmax) 

567 self.xview_range = None 

568 

569 def toggle_grid(self, event=None): 

570 self.panel.toggle_grid() 

571 

572 def set_roilist(self, mca=None): 

573 """ Add Roi names to roilist""" 

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

575 if mca is not None: 

576 for roi in mca.rois: 

577 name = bytes2str(roi.name.strip()) 

578 if len(name) > 0: 

579 self.wids['roilist'].Append(roi.name) 

580 

581 def clear_roihighlight(self, event=None): 

582 self.selected_roi = None 

583 try: 

584 self.roi_patch.remove() 

585 except: 

586 pass 

587 self.roi_patch = None 

588 self.wids['roiname'].SetValue('') 

589 self.draw() 

590 

591 def get_roiname(self): 

592 roiname = self.wids['roiname'].GetValue() 

593 if len(roiname) < 1: 

594 roiname = 'ROI 1' 

595 names = [str(r.name.lower()) for r in self.mca.rois] 

596 if str(roiname.lower()) in names: 

597 ix = 1 

598 while str(roiname.lower()) in names: 

599 roiname = "ROI %i" % (ix) 

600 ix += 1 

601 return roiname 

602 

603 def onNewROI(self, event=None): 

604 if (self.xmarker_left is None or 

605 self.xmarker_right is None or self.mca is None): 

606 return 

607 roiname = self.get_roiname() 

608 

609 names = [str(r.name.lower()) for r in self.mca.rois] 

610 if str(roiname.lower()) in names: 

611 msg = "Overwrite Definition of ROI {:s}?".format(roiname) 

612 if (wx.ID_YES != Popup(self, msg, 'Overwrite ROI?', style=wx.YES_NO)): 

613 return False 

614 

615 left, right = self.xmarker_left, self.xmarker_right 

616 if left > right: 

617 left, right = right, left 

618 self.mca.add_roi(name=roiname, left=left, right=right, sort=True) 

619 self.set_roilist(mca=self.mca) 

620 for roi in self.mca.rois: 

621 if roi.name.lower()==roiname: 

622 selected_roi = roi 

623 self.plot(self.xdata, self.ydata) 

624 self.onROI(label=roiname) 

625 if self.selected_elem is not None: 

626 self.onShowLines(elem=self.selected_elem) 

627 if self.roi_callback is not None: 

628 xrange = [self.mca.energy[left], self.mca.energy[right]] 

629 self.roi_callback(roiname, xrange=xrange, action='add', units='keV', roitype='XRF') 

630 return True 

631 

632 def onConfirmDelROI(self, event=None): 

633 roiname = self.wids['roiname'].GetValue() 

634 msg = "Delete ROI {:s}?".format(roiname) 

635 if (wx.ID_YES == Popup(self, msg, 'Delete ROI?', style=wx.YES_NO)): 

636 self.onDelROI() 

637 if self.roi_callback is not None: 

638 self.roi_callback(roiname, action='delete', roitype='XRF') 

639 

640 def onRenameROI(self, event=None): 

641 roiname = self.get_roiname() 

642 

643 if self.roilist_sel is not None: 

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

645 names[self.roilist_sel] = roiname 

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

647 for sname in names: 

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

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

650 

651 def onDelROI(self): 

652 roiname = self.wids['roiname'].GetValue() 

653 rdat = [] 

654 if self.mca is None: 

655 return 

656 for i in range(len(self.mca.rois)): 

657 roi = self.mca.rois.pop(0) 

658 if roi.name.lower() != roiname.lower(): 

659 rdat.append((roi.name, roi.left, roi.right)) 

660 

661 for name, left, right in rdat: 

662 self.mca.add_roi(name=name, left=left, right=right, sort=False) 

663 self.mca.rois.sort() 

664 self.set_roilist(mca=self.mca) 

665 self.wids['roiname'].SetValue('') 

666 try: 

667 self.roi_patch.remove() 

668 except: 

669 pass 

670 

671 self.plot(self.xdata, self.ydata) 

672 if self.selected_elem is not None: 

673 self.onShowLines(elem=self.selected_elem) 

674 

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

676 if left > right: 

677 return 

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

679 dt = self.mca.real_time 

680 nmsg, cmsg, rmsg = '', '', '' 

681 if len(name) > 0: 

682 nmsg = " %s" % name 

683 cmsg = " Cts={:10,.0f}".format(sum) 

684 if dt is not None and dt > 1.e-9: 

685 rmsg = " CPS={:10,.1f}".format(sum/dt) 

686 self.write_message("%s%s%s" % (nmsg, cmsg, rmsg), panel=panel) 

687 

688 def ShowROIPatch(self, left, right): 

689 """show colored XRF Patch: 

690 Note: ROIs larger than half the energy are not colored""" 

691 # xnpts = 1.0/len(self.mca.energy) 

692 # if xnpts*(right - left) > 0.5: 

693 # return 

694 

695 try: 

696 self.roi_patch.remove() 

697 except: 

698 pass 

699 

700 e = np.zeros(right-left+2) 

701 r = np.ones(right-left+2) 

702 e[1:-1] = self.mca.energy[left:right] 

703 r[1:-1] = self.mca.counts[left:right] 

704 e[0] = e[1] 

705 e[-1] = e[-2] 

706 self.roi_patch = self.panel.axes.fill_between(e, r, zorder=-20, 

707 color=self.conf.roi_fillcolor) 

708 

709 def onROI(self, event=None, label=None): 

710 if label is None and event is not None: 

711 label = event.GetString() 

712 self.roilist_sel = event.GetSelection() 

713 

714 self.wids['roiname'].SetValue(label) 

715 name, left, right= None, -1, -1 

716 label = bytes2str(label.lower().strip()) 

717 

718 self.selected_roi = None 

719 if self.mca is not None: 

720 for roi in self.mca.rois: 

721 if bytes2str(roi.name.lower())==label: 

722 left, right, name = roi.left, roi.right, roi.name 

723 elo = self.mca.energy[left] 

724 ehi = self.mca.energy[right] 

725 self.selected_roi = roi 

726 break 

727 if name is None or right == -1: 

728 return 

729 

730 self.ShowROIStatus(left, right, name=name) 

731 self.ShowROIPatch(left, right) 

732 

733 roi_msg1 = '[{:}:{:}]'.format(left, right) 

734 roi_msg2 = '[{:6.3f}:{:6.3f}]'.format(elo, ehi) 

735 roi_msg3 = '{:6.3f}, {:6.3f}'.format((elo+ehi)/2., (ehi - elo)) 

736 

737 self.energy_for_zoom = (elo+ehi)/2.0 

738 

739 self.wids['roi_msg1'].SetLabel(roi_msg1) 

740 self.wids['roi_msg2'].SetLabel(roi_msg2) 

741 self.wids['roi_msg3'].SetLabel(roi_msg3) 

742 

743 self.draw() 

744 self.panel.Refresh() 

745 

746 def onSaveROIs(self, event=None): 

747 pass 

748 

749 def onRestoreROIs(self, event=None): 

750 pass 

751 

752 def createCustomMenus(self): 

753 return 

754 

755 def createBaseMenus(self): 

756 fmenu = wx.Menu() 

757 MenuItem(self, fmenu, "&Read MCA Spectra File\tCtrl+O", 

758 "Read GSECARS MCA File", self.onReadMCAFile) 

759 

760 MenuItem(self, fmenu, "&Save MCA File\tCtrl+S", 

761 "Save GSECARS MCA File", self.onSaveMCAFile) 

762 MenuItem(self, fmenu, "&Save ASCII Column File\tCtrl+A", 

763 "Save Column File", self.onSaveColumnFile) 

764 

765 MenuItem(self, fmenu, "&Inspect \tCtrl+J", 

766 " wx inspection tool ", self.showInspectionTool) 

767 fmenu.AppendSeparator() 

768 # MenuItem(self, fmenu, "Save ROIs to File", 

769 # "Save ROIs to File", self.onSaveROIs) 

770 # MenuItem(self, fmenu, "Restore ROIs File", 

771 # "Read ROIs from File", self.onRestoreROIs) 

772 # fmenu.AppendSeparator() 

773 MenuItem(self, fmenu, 'Show Larch Buffer\tCtrl+L', 

774 'Show Larch Programming Buffer', 

775 self.onShowLarchBuffer) 

776 MenuItem(self, fmenu, "Save Plot\tCtrl+I", 

777 "Save PNG Image of Plot", self.onSavePNG) 

778 MenuItem(self, fmenu, "&Copy Plot\tCtrl+C", 

779 "Copy Plot Image to Clipboard", 

780 self.onCopyImage) 

781 MenuItem(self, fmenu, 'Page Setup...', 'Printer Setup', self.onPageSetup) 

782 MenuItem(self, fmenu, 'Print Preview...', 'Print Preview', self.onPrintPreview) 

783 MenuItem(self, fmenu, "&Print\tCtrl+P", "Print Plot", self.onPrint) 

784 

785 fmenu.AppendSeparator() 

786 MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) 

787 

788 omenu = wx.Menu() 

789 MenuItem(self, omenu, "Configure Colors", 

790 "Configure Colors", self.config_colors) 

791 MenuItem(self, omenu, "Configure X-ray Lines", 

792 "Configure which X-ray Lines are shown", self.config_xraylines) 

793 MenuItem(self, omenu, "Configure Plot\tCtrl+K", 

794 "Configure Plot Colors, etc", self.panel.configure) 

795 MenuItem(self, omenu, "Zoom Out\tCtrl+Z", 

796 "Zoom out to full data range", self.unzoom_all) 

797 MenuItem(self, omenu, "Toggle Grid\tCtrl+G", 

798 "Toggle Grid Display", self.toggle_grid) 

799 MenuItem(self, omenu, "Toggle Plot legend", 

800 "Toggle Plot Legend", self.onToggleLegend) 

801 omenu.AppendSeparator() 

802 MenuItem(self, omenu, "Hide X-ray Lines", 

803 "Hide all X-ray Lines", self.clear_lines) 

804 MenuItem(self, omenu, "Hide selected ROI ", 

805 "Hide selected ROI", self.clear_roihighlight) 

806 MenuItem(self, omenu, "Hide Markers ", 

807 "Hide cursor markers", self.clear_markers) 

808 MenuItem(self, omenu, "Hide XRF Background ", 

809 "Hide cursor markers", self.clear_background) 

810 

811 omenu.AppendSeparator() 

812 MenuItem(self, omenu, "Swap MCA and Background MCA", 

813 "Swap Foreground and Background MCAs", self.swap_mcas) 

814 MenuItem(self, omenu, "Close Background MCA", 

815 "Close Background MCA", self.close_bkg_mca) 

816 

817 amenu = wx.Menu() 

818 MenuItem(self, amenu, "Show Pileup Prediction", 

819 "Show Pileup Prediction", kind=wx.ITEM_CHECK, 

820 checked=False, action=self.onPileupPrediction) 

821 MenuItem(self, amenu, "Show Escape Prediction", 

822 "Show Escape Prediction", kind=wx.ITEM_CHECK, 

823 checked=False, action=self.onEscapePrediction) 

824 MenuItem(self, amenu, "&Calibrate Energy\tCtrl+E", 

825 "Calibrate Energy", self.onCalibrateEnergy) 

826 MenuItem(self, amenu, "Fit Spectrum\tCtrl+F", 

827 "Fit Spectrum for Elemental Contributiosn", 

828 self.onFitSpectrum) 

829 self._menus = [(fmenu, '&File'), 

830 (omenu, '&Options'), 

831 (amenu, '&Analysis')] 

832 

833 def createMenus(self): 

834 self.menubar = wx.MenuBar() 

835 self.createBaseMenus() 

836 self.createCustomMenus() 

837 for menu, title in self._menus: 

838 self.menubar.Append(menu, title) 

839 self.SetMenuBar(self.menubar) 

840 self.Bind(wx.EVT_CLOSE, self.onClose) 

841 

842 def onShowLarchBuffer(self, evt=None): 

843 if self.larch_buffer is not None: 

844 self.larch_buffer.Show() 

845 self.larch_buffer.Raise() 

846 

847 def onSavePNG(self, event=None): 

848 if self.panel is not None: 

849 self.panel.save_figure(event=event) 

850 

851 def onCopyImage(self, event=None): 

852 if self.panel is not None: 

853 self.panel.canvas.Copy_to_Clipboard(event=event) 

854 

855 def onPageSetup(self, event=None): 

856 if self.panel is not None: 

857 self.panel.PrintSetup(event=event) 

858 

859 def onPrintPreview(self, event=None): 

860 if self.panel is not None: 

861 self.panel.PrintPreview(event=event) 

862 

863 def onPrint(self, event=None): 

864 if self.panel is not None: 

865 self.panel.Print(event=event) 

866 

867 def onClose(self, event=None): 

868 try: 

869 if callable(self.exit_callback): 

870 self.exit_callback() 

871 except: 

872 pass 

873 try: 

874 if self.panel is not None: 

875 self.panel.win_config.Close(True) 

876 if self.panel is not None: 

877 self.panel.win_config.Destroy() 

878 except: 

879 pass 

880 

881 if hasattr(self.larch.symtable, '_plotter'): 

882 wx.CallAfter(self.larch.symtable._plotter.close_all_displays) 

883 

884 for name, wid in self.subframes.items(): 

885 if hasattr(wid, 'Destroy'): 

886 wx.CallAfter(wid.Destroy) 

887 self.Destroy() 

888 

889 def config_colors(self, event=None): 

890 """show configuration frame""" 

891 try: 

892 self.win_config.Raise() 

893 except: 

894 self.win_config = ColorsFrame(parent=self) 

895 

896 def config_xraylines(self, event=None): 

897 """show configuration frame""" 

898 try: 

899 self.win_config.Raise() 

900 except: 

901 self.win_config = XrayLinesFrame(parent=self) 

902 

903 def onToggleLegend(self, event=None): 

904 self.panel.conf.show_legend = not self.panel.conf.show_legend 

905 self.panel.conf.draw_legend() 

906 

907 def onKLM(self, event=None): 

908 """selected K, L, or M Markers""" 

909 if self.selected_elem is not None: 

910 self.onShowLines(elem = self.selected_elem) 

911 

912 def onToggleHold(self, event=None): 

913 if event.IsChecked(): 

914 self.wids['holdbtn'].SetLabel("Hide %s" % self.selected_elem) 

915 self.hold_lines = self.saved_lines[:] 

916 else: 

917 self.wids['holdbtn'].SetLabel("Hold %s" % self.selected_elem) 

918 self.hold_lines = None 

919 for m in self.hold_markers: 

920 try: 

921 m.remove() 

922 except: 

923 pass 

924 self.hold_markers = [] 

925 self.draw() 

926 

927 def onSelectXrayLine(self, evt=None): 

928 if self.wids['xray_lines'] is None: 

929 return 

930 if not self.wids['xray_lines'].HasSelection(): 

931 return 

932 item = self.wids['xray_lines'].GetSelectedRow() 

933 en = self.wids['xray_linesdata'][item] 

934 

935 if self.highlight_xrayline is not None: 

936 self.highlight_xrayline.remove() 

937 

938 self.energy_for_zoom = en 

939 self.highlight_xrayline = self.panel.axes.axvline(en, 

940 color=self.conf.emph_elinecolor, 

941 linewidth=2.5, zorder=-15) 

942 self.draw() 

943 

944 def onShowLines(self, event=None, elem=None): 

945 if elem is None: 

946 elem = event.GetString() 

947 

948 vline = self.panel.axes.axvline 

949 elines = self.larch.symtable._xray.xray_lines(elem) 

950 

951 self.selected_elem = elem 

952 self.clear_lines() 

953 self.energy_for_zoom = None 

954 xlines = self.wids['xray_lines'] 

955 if xlines is not None: 

956 xlines.DeleteAllItems() 

957 self.wids['xray_linesdata'] = [] 

958 minors, majors = [], [] 

959 conf = self.conf 

960 line_data = {} 

961 for line in (conf.K_major+conf.K_minor+conf.L_major+ 

962 conf.L_minor+conf.M_major): 

963 line_data[line] = line, -1, 0, '', '' 

964 if line in elines: 

965 dat = elines[line] 

966 line_data[line] = line, dat[0], dat[1], dat[2], dat[3] 

967 

968 if self.wids['kseries'].IsChecked(): 

969 majors.extend([line_data[l] for l in conf.K_major]) 

970 minors.extend([line_data[l] for l in conf.K_minor]) 

971 if self.wids['lseries'].IsChecked(): 

972 majors.extend([line_data[l] for l in conf.L_major]) 

973 minors.extend([line_data[l] for l in conf.L_minor]) 

974 if self.wids['mseries'].IsChecked(): 

975 majors.extend([line_data[l] for l in conf.M_major]) 

976 

977 self.saved_lines = majors[:] + minors[:] 

978 erange = [max(conf.e_min, self.xdata.min()), 

979 min(conf.e_max, self.xdata.max())] 

980 

981 view_mid, view_range, d1, d2 = self._getlims() 

982 view_emin = view_mid - view_range/2.0 

983 view_emax = view_mid + view_range/2.0 

984 for label, eev, frac, ilevel, flevel in majors: 

985 e = float(eev) * 0.001 

986 # print( 'Major ', label, eev, e, frac, ilevel, flevel) 

987 if (e >= erange[0] and e <= erange[1]): 

988 l = vline(e, color= self.conf.major_elinecolor, 

989 linewidth=1.50, zorder=-5) 

990 l.set_label(label) 

991 dat = (label, "%.4f" % e, "%.4f" % frac, 

992 "%s->%s" % (ilevel, flevel)) 

993 self.wids['xray_linesdata'].append(e) 

994 if xlines is not None: 

995 xlines.AppendItem(dat) 

996 

997 self.major_markers.append(l) 

998 if (self.energy_for_zoom is None and 

999 e > view_emin and e < view_emax): 

1000 self.energy_for_zoom = e 

1001 

1002 for label, eev, frac, ilevel, flevel in minors: 

1003 e = float(eev) * 0.001 

1004 if (e >= erange[0] and e <= erange[1]): 

1005 l = vline(e, color= self.conf.minor_elinecolor, 

1006 linewidth=1.25, zorder=-7) 

1007 l.set_label(label) 

1008 

1009 # dat = (label, "%.4f" % e, "%.4f" % frac, 

1010 # "%s->%s" % (ilevel, flevel)) 

1011 dat = (label, "%.4f" % e, "%.4f" % frac, 

1012 "%s->%s" % (ilevel, flevel)) 

1013 

1014 self.wids['xray_linesdata'].append(e) 

1015 if xlines is not None: 

1016 xlines.AppendItem(dat) 

1017 self.minor_markers.append(l) 

1018 

1019 if not self.wids['holdbtn'].GetValue(): 

1020 self.wids['holdbtn'].SetLabel("Hold %s" % elem) 

1021 elif self.hold_lines is not None: 

1022 for label, eev, frac, ilevel, flevel in self.hold_lines: 

1023 e = float(eev) * 0.001 

1024 if (e >= erange[0] and e <= erange[1]): 

1025 l = vline(e, color=self.conf.hold_elinecolor, 

1026 linewidth=1.5, zorder=-20, dashes=(3, 3)) 

1027 l.set_label(label) 

1028 self.hold_markers.append(l) 

1029 

1030 if xlines is not None: 

1031 xlines.Refresh() 

1032 

1033 edge_en = {} 

1034 for edge in ('K', 'M5', 'L3', 'L2', 'L1'): 

1035 edge_en[edge] = None 

1036 xex = self.larch.symtable._xray.xray_edge(elem, edge) 

1037 if xex is not None: 

1038 en = xex[0]*0.001 

1039 if en > erange[0] and en < erange[1]: 

1040 edge_en[edge] = en 

1041 out = '' 

1042 for key in ('M5', 'K'): 

1043 if edge_en[key] is not None: 

1044 out = "%s=%.3f" % (key, edge_en[key]) 

1045 if len(out) > 1: 

1046 self.wids['ptable'].set_subtitle(out, index=0) 

1047 s, v, out = [], [], '' 

1048 for key in ('L3', 'L2', 'L1'): 

1049 if edge_en[key] is not None: 

1050 s.append(key) 

1051 v.append("%.3f" % edge_en[key]) 

1052 if len(s) > 0: 

1053 out = "%s=%s" %(', '.join(s), ', '.join(v)) 

1054 self.wids['ptable'].set_subtitle(out, index=1) 

1055 

1056 self.draw() 

1057 

1058 def onPileupPrediction(self, event=None): 

1059 if event.IsChecked(): 

1060 self.mca.predict_pileup() 

1061 self.oplot(self.mca.energy, self.mca.pileup, 

1062 color=self.conf.pileup_color, label='pileup prediction') 

1063 else: 

1064 self.plotmca(self.mca) 

1065 

1066 def onEscapePrediction(self, event=None): 

1067 if event.IsChecked(): 

1068 self.mca.predict_escape() 

1069 self.oplot(self.mca.energy, self.mca.escape, 

1070 color=self.conf.escape_color, label='escape prediction') 

1071 else: 

1072 self.plotmca(self.mca) 

1073 

1074 

1075 def onYAxis(self, event=None): 

1076 self.show_yaxis = self.wids['show_yaxis'].IsChecked() 

1077 ax = self.panel.axes 

1078 ax.yaxis.set_major_formatter(FuncFormatter(self._formaty)) 

1079 ax.get_yaxis().set_visible(self.show_yaxis) 

1080 ax.spines['right'].set_visible(False) 

1081 ax.yaxis.set_ticks_position('left') 

1082 self.draw() 

1083 

1084 def _formaty(self, val, index=0, **kws): 

1085 try: 

1086 decade = int(np.log10(val)) 

1087 except: 

1088 decade = 0 

1089 scale = 10**decade 

1090 out = "%.1fe%i" % (val/scale, decade) 

1091 if abs(decade) < 1.9: 

1092 out = "%.1f" % val 

1093 elif abs(decade) < 3.9: 

1094 out = "%.0f" % val 

1095 return out 

1096 

1097 def onLogLinear(self, event=None): 

1098 self.ylog_scale = 'log' == event.GetString() 

1099 roiname = None 

1100 if self.selected_roi is not None: 

1101 roiname = self.selected_roi.name 

1102 self.plot(self.xdata, self.ydata) 

1103 if self.selected_elem is not None: 

1104 self.onShowLines(elem=self.selected_elem) 

1105 if roiname is not None: 

1106 self.onROI(label=roiname) 

1107 if self.y2data is not None: 

1108 self.oplot(self.x2data, self.y2data) 

1109 

1110 def plotmca(self, mca, title=None, set_title=True, as_mca2=False, 

1111 fullrange=False, init=False, **kws): 

1112 if as_mca2: 

1113 self.mca2 = mca 

1114 kws['new'] = False 

1115 else: 

1116 self.mca = mca 

1117 self.panel.conf.show_grid = False 

1118 xview_range = self.panel.axes.get_xlim() 

1119 

1120 if init or xview_range == (0.0, 1.0): 

1121 self.xview_range = (min(self.mca.energy), max(self.mca.energy)) 

1122 else: 

1123 self.xview_range = xview_range 

1124 

1125 atitles = [] 

1126 if self.mca is not None: 

1127 if getattr(self.mca, 'title', None) is not None: 

1128 atitles.append(bytes2str(self.mca.title)) 

1129 if getattr(self.mca, 'filename', None) is not None: 

1130 atitles.append(" File={:s}".format(self.mca.filename)) 

1131 if getattr(self.mca, 'npixels', None) is not None: 

1132 atitles.append(" {:.0f} Pixels".format(self.mca.npixels)) 

1133 if getattr(self.mca, 'real_time', None) is not None: 

1134 try: 

1135 rtime_str = " RealTime={:.2f} sec".format(self.mca.real_time) 

1136 except ValueError: 

1137 rtime_str = " RealTime= %s sec".format(str(self.mca.real_time)) 

1138 atitles.append(rtime_str) 

1139 

1140 try: 

1141 self.plot(self.mca.energy, self.mca.counts, 

1142 mca=self.mca, **kws) 

1143 except ValueError: 

1144 pass 

1145 if as_mca2: 

1146 if getattr(self.mca2, 'title', None) is not None: 

1147 atitles.append(" BG={:s}".format(self.mca2.title)) 

1148 elif getattr(self.mca2, 'filename', None) is not None: 

1149 atitles.append(" BG_File={:s}".format(self.mca2.filename)) 

1150 if getattr(self.mca, 'real_time', None) is not None: 

1151 atitles.append(" BG_RealTime={:.2f} sec".format(self.mca2.real_time)) 

1152 

1153 self.oplot(self.mca2.energy, self.mca2.counts, 

1154 mca=self.mca2, **kws) 

1155 if title is None: 

1156 title = ' '.join(atitles) 

1157 if set_title: 

1158 self.SetTitle(title) 

1159 

1160 def plot(self, x, y=None, mca=None, init=False, with_rois=True, **kws): 

1161 if mca is not None: 

1162 self.mca = mca 

1163 mca = self.mca 

1164 panel = self.panel 

1165 

1166 panel.yformatter = self._formaty 

1167 panel.axes.get_yaxis().set_visible(False) 

1168 kwargs = {'xmin': 0, 

1169 'linewidth': 2.5, 

1170 'delay_draw': True, 

1171 'grid': panel.conf.show_grid, 

1172 'ylog_scale': self.ylog_scale, 

1173 'xlabel': 'E (keV)', 

1174 'axes_style': 'bottom', 

1175 'color': self.conf.spectra_color} 

1176 kwargs.update(kws) 

1177 

1178 self.xdata = 1.0*x[:] 

1179 self.ydata = 1.0*y[:] 

1180 self.ydata[np.where(self.ydata<1.e-9)] = 1.e-9 

1181 ydat = self.ydata 

1182 kwargs['ymax'] = max(ydat)*1.25 

1183 kwargs['ymin'] = 0.9 

1184 kwargs['xmax'] = max(self.xdata) 

1185 kwargs['xmin'] = min(self.xdata) 

1186 if self.xview_range is not None: 

1187 kwargs['xmin'] = self.xview_range[0] 

1188 kwargs['xmax'] = self.xview_range[1] 

1189 

1190 panel.plot(x, ydat, label='spectrum', **kwargs) 

1191 if with_rois and mca is not None: 

1192 if not self.rois_shown: 

1193 self.set_roilist(mca=mca) 

1194 yroi = -1.0*np.ones(len(y)) 

1195 max_width = 0.5*len(self.mca.energy) # suppress very large ROIs 

1196 for r in mca.rois: 

1197 if ((r.left, r.right) in ((0, 0), (-1, -1)) or 

1198 (r.right - r.left) > max_width): 

1199 continue 

1200 yroi[r.left:r.right] = y[r.left:r.right] 

1201 yroi = np.ma.masked_less(yroi, 0) 

1202 xroi = 1.0*x[:] 

1203 xroi[yroi< 0.0] = np.nan 

1204 if yroi.max() > 0: 

1205 kwargs['color'] = self.conf.roi_color 

1206 panel.oplot(xroi, yroi, label='rois', **kwargs) 

1207 yscale = {False:'linear', True:'log'}[self.ylog_scale] 

1208 panel.set_viewlimits() 

1209 panel.set_logscale(yscale=yscale) 

1210 panel.axes.get_yaxis().set_visible(self.show_yaxis) 

1211 panel.cursor_mode = 'zoom' 

1212 self.draw() 

1213 panel.canvas.Refresh() 

1214 

1215 

1216 def update_mca(self, counts, energy=None, with_rois=True, 

1217 is_mca2=False, draw=True): 

1218 """update counts (and optionally energy) for mca, and update plot""" 

1219 mca = self.mca 

1220 ix = 0 

1221 if is_mca2: 

1222 mca = self.mca2 

1223 ix = 2 

1224 mca.counts = 1.0*counts[:] 

1225 if energy is not None: 

1226 mca.energy = 1.0*energy[:] 

1227 xnpts = 1.0/len(energy) 

1228 nrois = len(mca.rois) 

1229 if not is_mca2 and with_rois and nrois > 0: 

1230 yroi = -1*np.ones(len(counts)) 

1231 for r in mca.rois: 

1232 if xnpts*(r.right - r.left) > 0.5: 

1233 continue 

1234 yroi[r.left:r.right] = counts[r.left:r.right] 

1235 yroi = np.ma.masked_less(yroi, 0) 

1236 self.panel.update_line(1, mca.energy, yroi, draw=False, 

1237 update_limits=False) 

1238 

1239 self.panel.update_line(ix, mca.energy, counts, 

1240 draw=False, update_limits=False) 

1241 

1242 max_counts = max_counts2 = max(self.mca.counts) 

1243 try: 

1244 max_counts2 = max(self.mca2.counts) 

1245 except: 

1246 pass 

1247 

1248 self.panel.axes.set_ylim(0.9, 1.25*max(max_counts, max_counts2)) 

1249 if mca == self.mca: 

1250 self.ydata = 1.0*counts[:] 

1251 self.update_status() 

1252 if draw: self.draw() 

1253 

1254 def oplot(self, x, y, color='darkgreen', label='spectrum2', 

1255 mca=None, zorder=-2, **kws): 

1256 if mca is not None: 

1257 self.mca2 = mca 

1258 

1259 self.x2data = 1.0*x[:] 

1260 self.y2data = 1.0*y[:] 

1261 if hasattr(self, 'ydata'): 

1262 ymax = max(max(self.ydata), max(y))*1.25 

1263 else: 

1264 ymax = max(y)*1.25 

1265 

1266 kws.update({'zorder': zorder, 'label': label, 

1267 'ymax' : ymax, 'axes_style': 'bottom', 

1268 'ylog_scale': self.ylog_scale}) 

1269 self.panel.oplot(self.x2data, self.y2data, color=color, **kws) 

1270 

1271 def swap_mcas(self, event=None): 

1272 if self.mca2 is None: 

1273 return 

1274 self.mca, self.mca2 = self.mca2, self.mca 

1275 xrfgroup = self.larch.symtable.get_group(XRFGROUP) 

1276 _mca = getattr(xrfgroup, '_mca', '') 

1277 _mca2 = getattr(xrfgroup, '_mca2', '') 

1278 setattr(xrfgroup, '_mca2', _mca) 

1279 setattr(xrfgroup, '_mca', _mca2) 

1280 

1281 self.plotmca(self.mca) 

1282 self.plotmca(self.mca2, as_mca2=True) 

1283 

1284 def close_bkg_mca(self, event=None): 

1285 self.mca2 = None 

1286 xrfgroup = self.larch.symtable.get_group(XRFGROUP) 

1287 setattr(xrfgroup, '_mca2', '') 

1288 self.plotmca(self.mca) 

1289 

1290 def onReadMCAFile(self, event=None): 

1291 dlg = wx.FileDialog(self, message="Open MCA File for reading", 

1292 defaultDir=get_cwd(), 

1293 wildcard=FILE_WILDCARDS, 

1294 style = wx.FD_OPEN|wx.FD_CHANGE_DIR) 

1295 

1296 filename = None 

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

1298 filename = Path(dlg.GetPath()).absolute().as_posix() 

1299 dlg.Destroy() 

1300 

1301 if filename is None: 

1302 return 

1303 if self.mca is not None: 

1304 self.mca2 = copy.deepcopy(self.mca) 

1305 

1306 self.add_mca(GSEMCA_File(filename), filename=filename) 

1307 

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

1309 deffile = '' 

1310 if getattr(self.mca, 'sourcefile', None) is not None: 

1311 deffile = "%s%s" % (deffile, self.mca.sourcefile) 

1312 elif getattr(self.mca, 'filename', None) is not None: 

1313 deffile = "%s%s" % (deffile, self.mca.filename) 

1314 if getattr(self.mca, 'areaname', None) is not None: 

1315 deffile = "%s_%s" % (deffile, self.mca.areaname) 

1316 if deffile == '': 

1317 deffile ='test' 

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

1319 deffile = deffile + '.mca' 

1320 

1321 deffile = fix_filename(Path(deffile).name) 

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

1323 default_file=deffile, 

1324 wildcard=FILE_WILDCARDS) 

1325 if outfile is not None: 

1326 self.mca.save_mcafile(outfile) 

1327 

1328 

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

1330 deffile = '' 

1331 if getattr(self.mca, 'sourcefile', None) is not None: 

1332 deffile = "%s%s" % (deffile, self.mca.sourcefile) 

1333 elif getattr(self.mca, 'filename', None) is not None: 

1334 deffile = "%s%s" % (deffile, self.mca.filename) 

1335 

1336 if getattr(self.mca, 'areaname', None) is not None: 

1337 deffile = "%s_%s" % (deffile, self.mca.areaname) 

1338 if deffile == '': 

1339 deffile ='test' 

1340 if not deffile.endswith('.dat'): 

1341 deffile = deffile + '.dat' 

1342 

1343 deffile = fix_filename(Path(deffile).name) 

1344 ASCII_WILDCARDS = "Data File (*.dat)|*.dat|All files (*.*)|*.*" 

1345 outfile = FileSave(self, "Save ASCII File for MCA Data", 

1346 default_file=deffile, 

1347 wildcard=ASCII_WILDCARDS) 

1348 if outfile is not None: 

1349 self.mca.save_ascii(outfile) 

1350 

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

1352 try: 

1353 self.win_calib.Raise() 

1354 except: 

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

1356 callback=self.onCalibrationChange) 

1357 

1358 def onCalibrationChange(self, mca): 

1359 """update whenn mca changed calibration""" 

1360 self.plotmca(mca) 

1361 

1362 def onFitSpectrum(self, event=None, **kws): 

1363 try: 

1364 self.win_fit.Raise() 

1365 except: 

1366 self.win_fit = FitSpectraFrame(self) 

1367 

1368 def write_message(self, s, panel=0): 

1369 """write a message to the Status Bar""" 

1370 self.SetStatusText(s, panel) 

1371 

1372 def showInspectionTool(self, event=None): 

1373 app = wx.GetApp() 

1374 app.ShowInspectionTool() 

1375 

1376 def onAbout(self, event=None): 

1377 dlg = wx.MessageDialog(self, 

1378 """XRF Spectral Viewer 

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

1380 """, 

1381 "About XRF Viewer", 

1382 wx.OK | wx.ICON_INFORMATION) 

1383 dlg.ShowModal() 

1384 dlg.Destroy() 

1385 

1386 def onReadFile(self, event=None): 

1387 dlg = wx.FileDialog(self, message="Read MCA File", 

1388 defaultDir=get_cwd(), 

1389 wildcard=FILE_WILDCARDS, 

1390 style=wx.FD_OPEN) 

1391 path, re1ad = None, False 

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

1393 read = True 

1394 path = dlg.GetPath().replace('\\', '/') 

1395 if path in self.filemap: 

1396 read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path, 

1397 'Re-read file?', style=wx.YES_NO)) 

1398 dlg.Destroy() 

1399 return path 

1400 

1401 

1402class XRFApp(LarchWxApp): 

1403 def __init__(self, filename=None, **kws): 

1404 self.filename = filename 

1405 LarchWxApp.__init__(self, **kws) 

1406 

1407 def createApp(self): 

1408 frame = XRFDisplayFrame(filename=self.filename) 

1409 frame.Show() 

1410 self.SetTopWindow(frame) 

1411 return True