Coverage for /Users/Newville/Codes/xraylarch/larch/wxlib/xrfdisplay.py: 10%

1056 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-09 10:08 -0600

1#!/usr/bin/env python 

2""" 

3GUI Frame for XRF display, reading larch MCA group 

4 

5""" 

6import sys 

7import os 

8import time 

9import copy 

10from functools import partial 

11 

12import wx 

13import wx.lib.mixins.inspection 

14import wx.lib.scrolledpanel as scrolled 

15import wx.dataview as dv 

16import wx.lib.colourselect as csel 

17 

18import numpy as np 

19import matplotlib 

20from matplotlib.ticker import LogFormatter, FuncFormatter 

21 

22from wxmplot import PlotPanel 

23from wxutils import (SimpleText, EditableListBox, Font, pack, Popup, 

24 get_icon, SetTip, Button, Check, MenuItem, Choice, 

25 FileOpen, FileSave, fix_filename, HLine, GridPanel, 

26 CEN, LEFT, RIGHT, PeriodicTablePanel) 

27from pyshortcuts import platform 

28from . import FONTSIZE, FONTSIZE_FW 

29from ..math import index_of 

30from ..utils import bytes2str, debugtime, get_cwd 

31from ..io import GSEMCA_File 

32from ..site_config import icondir 

33from ..interpreter import Interpreter 

34 

35from .gui_utils import LarchWxApp 

36from .larchframe import LarchFrame 

37# from .periodictable import PeriodicTablePanel 

38 

39from .xrfdisplay_utils import (XRFCalibrationFrame, ColorsFrame, 

40 XrayLinesFrame, XRFDisplayConfig, XRFGROUP, 

41 MAKE_XRFGROUP_CMD, next_mcaname) 

42 

43from .xrfdisplay_fitpeaks import FitSpectraFrame 

44 

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

46FILE_ALREADY_READ = """The File 

47 '%s' 

48has already been read. 

49""" 

50 

51ICON_FILE = 'ptable.ico' 

52 

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

54 

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

56 if style is None: 

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

58 if colour is None: 

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

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

61 colour=colour, style=style) 

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

63 return this 

64 

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

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

67 

68 

69class XRFDisplayFrame(wx.Frame): 

70 _about = """XRF Spectral Viewer 

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

72 """ 

73 main_title = 'XRF Display' 

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

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

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

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

78 

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

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

81 title=title, size=size, **kws) 

82 self.conf = XRFDisplayConfig() 

83 self.subframes = {} 

84 self.data = None 

85 self.title = title 

86 self.roi_callback = roi_callback 

87 self.plotframe = None 

88 self.wids = {} 

89 self.larch = _larch 

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

91 self.larch_buffer = None 

92 else: 

93 self.larch_buffer = parent 

94 if not isinstance(parent, LarchFrame): 

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

96 is_standalone=False, with_raise=False) 

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

98 self.larch = self.larch_buffer.larchshell 

99 self.init_larch() 

100 

101 self.exit_callback = exit_callback 

102 self.roi_patch = None 

103 self.selected_roi = None 

104 self.roilist_sel = None 

105 self.selected_elem = None 

106 self.mca = None 

107 self.mca2 = None 

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

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

110 self.x2data = None 

111 self.y2data = None 

112 self.rois_shown = False 

113 self.mca_index = 0 

114 self.major_markers = [] 

115 self.minor_markers = [] 

116 self.hold_markers = [] 

117 

118 self.hold_lines = None 

119 self.saved_lines = None 

120 self.energy_for_zoom = None 

121 self.xview_range = None 

122 self.show_yaxis = False 

123 self.xmarker_left = None 

124 self.xmarker_right = None 

125 

126 self.highlight_xrayline = None 

127 self.highlight_xrayline = None 

128 self.cursor_markers = [None, None] 

129 self.ylog_scale = True 

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

131 

132 self._menus = [] 

133 self.createMainPanel() 

134 self.createMenus() 

135 self.SetFont(Font(9, serif=True)) 

136 self.statusbar = self.CreateStatusBar(4) 

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

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

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

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

141 if filename is not None: 

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

143 

144 

145 def ignoreEvent(self, event=None): 

146 pass 

147 

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

149 if event is None: 

150 return 

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

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

153 try: 

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

155 except: 

156 pass 

157 ix = x 

158 if self.mca is not None: 

159 try: 

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

161 except TypeError: 

162 pass 

163 

164 if side == 'right': 

165 self.xmarker_right = ix 

166 elif side == 'left': 

167 self.xmarker_left = ix 

168 

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

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

171 self.xmarker_left = min(ix1, ix2) 

172 self.xmarker_right = max(ix1, ix2) 

173 

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

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

176 self.update_status() 

177 self.draw() 

178 

179 def clear_lines(self, evt=None): 

180 "remove all Line Markers" 

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

182 try: 

183 m.remove() 

184 except: 

185 pass 

186 if self.highlight_xrayline is not None: 

187 try: 

188 self.highlight_xrayline.remove() 

189 except: 

190 pass 

191 

192 self.highlight_xrayline = None 

193 self.major_markers = [] 

194 self.minor_markers = [] 

195 self.hold_markers = [] 

196 self.draw() 

197 

198 def draw(self): 

199 try: 

200 self.panel.canvas.draw() 

201 except: 

202 pass 

203 

204 def clear_markers(self, evt=None): 

205 "remove all Cursor Markers" 

206 for m in self.cursor_markers: 

207 if m is not None: 

208 m.remove() 

209 self.cursor_markers = [None, None] 

210 self.xmarker_left = None 

211 self.xmarker_right = None 

212 self.draw() 

213 

214 def clear_background(self, evt=None): 

215 "remove XRF background" 

216 self.mca2 = None 

217 self.plotmca(self.mca) 

218 

219 def update_status(self): 

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

221 if (self.xmarker_left is None and 

222 self.xmarker_right is None and 

223 self.selected_roi is None): 

224 return 

225 

226 log = np.log10 

227 axes= self.panel.axes 

228 def draw_ymarker_range(idx, x, y): 

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

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

231 if y < 1.0: y = 1.0 

232 if self.ylog_scale: 

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

234 if y1 < 0.0: y1 = 0.0 

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

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

237 try: 

238 self.cursor_markers[idx].remove() 

239 except: 

240 pass 

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

242 color=self.conf.marker_color) 

243 

244 if self.xmarker_left is not None: 

245 ix = self.xmarker_left 

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

247 draw_ymarker_range(0, x, y) 

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

249 if self.xmarker_right is not None: 

250 ix = self.xmarker_right 

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

252 draw_ymarker_range(1, x, y) 

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

254 

255 if self.mca is None: 

256 return 

257 

258 if (self.xmarker_left is not None and 

259 self.xmarker_right is not None): 

260 self.ShowROIStatus(self.xmarker_left, 

261 self.xmarker_right, 

262 name='', panel=3) 

263 

264 if self.selected_roi is not None: 

265 roi = self.selected_roi 

266 left, right = roi.left, roi.right 

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

268 self.ShowROIPatch(left, right) 

269 

270 def createPlotPanel(self): 

271 """mca plot window""" 

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

273 with_data_process=False, 

274 output_title='test.xrf', 

275 messenger=self.write_message) 

276 pan.SetSize((650, 350)) 

277 

278 pan.conf.grid_color='#E5E5C0' 

279 pan.conf.show_grid = False 

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

281 pan.conf.labelfont.set_size(6) 

282 pan.conf.labelfont.set_size(6) 

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

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

285 leftup = self.ignoreEvent, 

286 leftdown = self.on_cursor, 

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

288 return pan 

289 

290 def createControlPanel(self): 

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

292 ptable_fontsize = 11 if platform=='darwin' else 9 

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

294 tooltip_msg='Select Element for KLM Lines', 

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

296 self.wids['ptable'] = ptable 

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

298 

299 labstyle = wx.ALIGN_LEFT|wx.EXPAND 

300 ctrlstyle = wx.ALIGN_LEFT 

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

302 Font9 = Font(9) 

303 Font10 = Font(10) 

304 Font11 = Font(11) 

305 # 

306 arrowpanel = wx.Panel(ctrlpanel) 

307 ssizer = wx.BoxSizer(wx.HORIZONTAL) 

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

309 ('leftarrow', 'left'), 

310 ('rightarrow', 'right'), 

311 ('downarrow', 'down')): 

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

313 get_icon(wname), 

314 style=wx.NO_BORDER) 

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

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

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

318 

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

320 size=(85, -1)) 

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

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

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

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

325 

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

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

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

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

330 pack(arrowpanel, ssizer) 

331 

332 # roi section... 

333 rsizer = wx.GridBagSizer(4, 6) 

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

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

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

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

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

339 

340 # 

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

342 zsizer = wx.BoxSizer(wx.HORIZONTAL) 

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

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

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

346 

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

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

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

350 pack(roibtns, zsizer) 

351 

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

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

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

355 m = '' 

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

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

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

359 

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

361 (0, 0), (1, 3), labstyle) 

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

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

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

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

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

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

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

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

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

371 wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT) 

372 rsizer.SetHGap(1) 

373 

374 pack(roipanel, rsizer) 

375 # end roi section 

376 

377 # y scale 

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

379 ysizer = wx.BoxSizer(wx.HORIZONTAL) 

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

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

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

383 action=self.onLogLinear) 

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

385 default=False) 

386 self.wids['show_yaxis'] = yaxis 

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

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

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

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

391 pack(yscalepanel, ysizer) 

392 

393 # zoom buttons 

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

395 zsizer = wx.BoxSizer(wx.HORIZONTAL) 

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

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

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

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

400 

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

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

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

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

405 pack(zoompanel, zsizer) 

406 

407 self.wids['xray_lines'] = None 

408 

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

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

411 xlines.SetFont(self.font_fixedwidth) 

412 self.wids['xray_lines'] = xlines 

413 

414 xw = (60, 100, 80, 100) 

415 if platform=='win': 

416 xw = (65, 105, 85, 100) 

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

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

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

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

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

422 this = xlines.Columns[col] 

423 this.Sortable = False 

424 align = RIGHT 

425 if col in (0, 3): 

426 align = wx.ALIGN_LEFT 

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

428 

429 xlines.SetMinSize((300, 240)) 

430 xlines.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, 

431 self.onSelectXrayLine) 

432 store = xlines.GetStore() 

433 

434 # main layout 

435 # may have to adjust comparison.... 

436 

437 sizer = wx.BoxSizer(wx.VERTICAL) 

438 sizer.Add(roipanel, 0, labstyle) 

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

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

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

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

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

444 sizer.Add(arrowpanel, 0, labstyle) 

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

446 

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

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

449 

450 pack(ctrlpanel, sizer) 

451 return ctrlpanel 

452 

453 def createMainPanel(self): 

454 ctrlpanel = self.createControlPanel() 

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

456 plotpanel.yformatter = self._formaty 

457 

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

459 cx, cy = ctrlpanel.GetBestVirtualSize() 

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

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

462 

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

464 sizer = wx.BoxSizer(wx.HORIZONTAL) 

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

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

467 

468 self.SetMinSize((450, 150)) 

469 pack(self, sizer) 

470 self.set_roilist(mca=None) 

471 

472 def init_larch(self): 

473 symtab = self.larch.symtable 

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

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

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

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

478 

479 if not symtab.has_group(XRFGROUP): 

480 self.larch.eval(MAKE_XRFGROUP_CMD) 

481 

482 fico = os.path.join(icondir, ICON_FILE) 

483 try: 

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

485 except: 

486 pass 

487 

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

489 if as_mca2: 

490 self.mca2 = mca 

491 else: 

492 self.mca2 = self.mca 

493 self.mca = mca 

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

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

496 mcaname = next_mcaname(self.larch) 

497 if filename is not None: 

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

499 name=mcaname, 

500 filename=filename)) 

501 if label is None: 

502 label = filename 

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

504 label = mca.filename 

505 if label is None: 

506 label = mcaname 

507 self.mca.label = label 

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

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

510 setattr(xrfgroup, '_mca', mcaname) 

511 setattr(xrfgroup, mcaname, mca) 

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

513 if plot: 

514 self.plotmca(self.mca) 

515 if as_mca2: 

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

517 

518 def _getlims(self): 

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

520 erange = emax-emin 

521 emid = (emax+emin)/2.0 

522 if self.energy_for_zoom is not None: 

523 emid = self.energy_for_zoom 

524 dmin, dmax = emin, emax 

525 drange = erange 

526 if self.mca is not None: 

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

528 return (emid, erange, dmin, dmax) 

529 

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

531 if not keep_zoom: 

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

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

534 self.xview_range = [e1, e2] 

535 self.draw() 

536 

537 def onPanLo(self, event=None): 

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

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

540 e2 = min(dmax, e1 + erange) 

541 self._set_xview(e1, e2) 

542 

543 def onPanHi(self, event=None): 

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

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

546 e1 = max(dmin, e2-erange) 

547 self._set_xview(e1, e2) 

548 

549 def onZoomIn(self, event=None): 

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

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

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

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

554 

555 def onZoomOut(self, event=None): 

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

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

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

559 self._set_xview(e1, e2) 

560 

561 def unzoom_all(self, event=None): 

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

563 self._set_xview(dmin, dmax) 

564 self.xview_range = None 

565 

566 def toggle_grid(self, event=None): 

567 self.panel.toggle_grid() 

568 

569 def set_roilist(self, mca=None): 

570 """ Add Roi names to roilist""" 

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

572 if mca is not None: 

573 for roi in mca.rois: 

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

575 if len(name) > 0: 

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

577 

578 def clear_roihighlight(self, event=None): 

579 self.selected_roi = None 

580 try: 

581 self.roi_patch.remove() 

582 except: 

583 pass 

584 self.roi_patch = None 

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

586 self.draw() 

587 

588 def get_roiname(self): 

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

590 if len(roiname) < 1: 

591 roiname = 'ROI 1' 

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

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

594 ix = 1 

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

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

597 ix += 1 

598 return roiname 

599 

600 def onNewROI(self, event=None): 

601 if (self.xmarker_left is None or 

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

603 return 

604 roiname = self.get_roiname() 

605 

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

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

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

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

610 return False 

611 

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

613 if left > right: 

614 left, right = right, left 

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

616 self.set_roilist(mca=self.mca) 

617 for roi in self.mca.rois: 

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

619 selected_roi = roi 

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

621 self.onROI(label=roiname) 

622 if self.selected_elem is not None: 

623 self.onShowLines(elem=self.selected_elem) 

624 if self.roi_callback is not None: 

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

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

627 return True 

628 

629 def onConfirmDelROI(self, event=None): 

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

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

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

633 self.onDelROI() 

634 if self.roi_callback is not None: 

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

636 

637 def onRenameROI(self, event=None): 

638 roiname = self.get_roiname() 

639 

640 if self.roilist_sel is not None: 

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

642 names[self.roilist_sel] = roiname 

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

644 for sname in names: 

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

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

647 

648 def onDelROI(self): 

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

650 rdat = [] 

651 if self.mca is None: 

652 return 

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

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

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

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

657 

658 for name, left, right in rdat: 

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

660 self.mca.rois.sort() 

661 self.set_roilist(mca=self.mca) 

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

663 try: 

664 self.roi_patch.remove() 

665 except: 

666 pass 

667 

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

669 if self.selected_elem is not None: 

670 self.onShowLines(elem=self.selected_elem) 

671 

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

673 if left > right: 

674 return 

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

676 dt = self.mca.real_time 

677 nmsg, cmsg, rmsg = '', '', '' 

678 if len(name) > 0: 

679 nmsg = " %s" % name 

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

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

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

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

684 

685 def ShowROIPatch(self, left, right): 

686 """show colored XRF Patch: 

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

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

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

690 # return 

691 

692 try: 

693 self.roi_patch.remove() 

694 except: 

695 pass 

696 

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

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

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

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

701 e[0] = e[1] 

702 e[-1] = e[-2] 

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

704 color=self.conf.roi_fillcolor) 

705 

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

707 if label is None and event is not None: 

708 label = event.GetString() 

709 self.roilist_sel = event.GetSelection() 

710 

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

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

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

714 

715 self.selected_roi = None 

716 if self.mca is not None: 

717 for roi in self.mca.rois: 

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

719 left, right, name = roi.left, roi.right, roi.name 

720 elo = self.mca.energy[left] 

721 ehi = self.mca.energy[right] 

722 self.selected_roi = roi 

723 break 

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

725 return 

726 

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

728 self.ShowROIPatch(left, right) 

729 

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

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

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

733 

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

735 

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

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

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

739 

740 self.draw() 

741 self.panel.Refresh() 

742 

743 def onSaveROIs(self, event=None): 

744 pass 

745 

746 def onRestoreROIs(self, event=None): 

747 pass 

748 

749 def createCustomMenus(self): 

750 return 

751 

752 def createBaseMenus(self): 

753 fmenu = wx.Menu() 

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

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

756 

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

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

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

760 "Save Column File", self.onSaveColumnFile) 

761 

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

763 " wx inspection tool ", self.showInspectionTool) 

764 fmenu.AppendSeparator() 

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

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

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

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

769 # fmenu.AppendSeparator() 

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

771 'Show Larch Programming Buffer', 

772 self.onShowLarchBuffer) 

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

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

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

776 "Copy Plot Image to Clipboard", 

777 self.onCopyImage) 

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

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

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

781 

782 fmenu.AppendSeparator() 

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

784 

785 omenu = wx.Menu() 

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

787 "Configure Colors", self.config_colors) 

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

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

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

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

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

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

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

795 "Toggle Grid Display", self.toggle_grid) 

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

797 "Toggle Plot Legend", self.onToggleLegend) 

798 omenu.AppendSeparator() 

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

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

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

802 "Hide selected ROI", self.clear_roihighlight) 

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

804 "Hide cursor markers", self.clear_markers) 

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

806 "Hide cursor markers", self.clear_background) 

807 

808 omenu.AppendSeparator() 

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

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

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

812 "Close Background MCA", self.close_bkg_mca) 

813 

814 amenu = wx.Menu() 

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

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

817 checked=False, action=self.onPileupPrediction) 

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

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

820 checked=False, action=self.onEscapePrediction) 

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

822 "Calibrate Energy", self.onCalibrateEnergy) 

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

824 "Fit Spectrum for Elemental Contributiosn", 

825 self.onFitSpectrum) 

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

827 (omenu, '&Options'), 

828 (amenu, '&Analysis')] 

829 

830 def createMenus(self): 

831 self.menubar = wx.MenuBar() 

832 self.createBaseMenus() 

833 self.createCustomMenus() 

834 for menu, title in self._menus: 

835 self.menubar.Append(menu, title) 

836 self.SetMenuBar(self.menubar) 

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

838 

839 def onShowLarchBuffer(self, evt=None): 

840 if self.larch_buffer is not None: 

841 self.larch_buffer.Show() 

842 self.larch_buffer.Raise() 

843 

844 def onSavePNG(self, event=None): 

845 if self.panel is not None: 

846 self.panel.save_figure(event=event) 

847 

848 def onCopyImage(self, event=None): 

849 if self.panel is not None: 

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

851 

852 def onPageSetup(self, event=None): 

853 if self.panel is not None: 

854 self.panel.PrintSetup(event=event) 

855 

856 def onPrintPreview(self, event=None): 

857 if self.panel is not None: 

858 self.panel.PrintPreview(event=event) 

859 

860 def onPrint(self, event=None): 

861 if self.panel is not None: 

862 self.panel.Print(event=event) 

863 

864 def onClose(self, event=None): 

865 try: 

866 if callable(self.exit_callback): 

867 self.exit_callback() 

868 except: 

869 pass 

870 try: 

871 if self.panel is not None: 

872 self.panel.win_config.Close(True) 

873 if self.panel is not None: 

874 self.panel.win_config.Destroy() 

875 except: 

876 pass 

877 

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

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

880 

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

882 if hasattr(wid, 'Destroy'): 

883 wx.CallAfter(wid.Destroy) 

884 self.Destroy() 

885 

886 def config_colors(self, event=None): 

887 """show configuration frame""" 

888 try: 

889 self.win_config.Raise() 

890 except: 

891 self.win_config = ColorsFrame(parent=self) 

892 

893 def config_xraylines(self, event=None): 

894 """show configuration frame""" 

895 try: 

896 self.win_config.Raise() 

897 except: 

898 self.win_config = XrayLinesFrame(parent=self) 

899 

900 def onToggleLegend(self, event=None): 

901 self.panel.conf.show_legend = not self.panel.conf.show_legend 

902 self.panel.conf.draw_legend() 

903 

904 def onKLM(self, event=None): 

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

906 if self.selected_elem is not None: 

907 self.onShowLines(elem = self.selected_elem) 

908 

909 def onToggleHold(self, event=None): 

910 if event.IsChecked(): 

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

912 self.hold_lines = self.saved_lines[:] 

913 else: 

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

915 self.hold_lines = None 

916 for m in self.hold_markers: 

917 try: 

918 m.remove() 

919 except: 

920 pass 

921 self.hold_markers = [] 

922 self.draw() 

923 

924 def onSelectXrayLine(self, evt=None): 

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

926 return 

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

928 return 

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

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

931 

932 if self.highlight_xrayline is not None: 

933 self.highlight_xrayline.remove() 

934 

935 self.energy_for_zoom = en 

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

937 color=self.conf.emph_elinecolor, 

938 linewidth=2.5, zorder=-15) 

939 self.draw() 

940 

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

942 if elem is None: 

943 elem = event.GetString() 

944 

945 vline = self.panel.axes.axvline 

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

947 

948 self.selected_elem = elem 

949 self.clear_lines() 

950 self.energy_for_zoom = None 

951 xlines = self.wids['xray_lines'] 

952 if xlines is not None: 

953 xlines.DeleteAllItems() 

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

955 minors, majors = [], [] 

956 conf = self.conf 

957 line_data = {} 

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

959 conf.L_minor+conf.M_major): 

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

961 if line in elines: 

962 dat = elines[line] 

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

964 

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

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

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

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

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

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

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

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

973 

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

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

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

977 

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

979 view_emin = view_mid - view_range/2.0 

980 view_emax = view_mid + view_range/2.0 

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

982 e = float(eev) * 0.001 

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

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

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

986 linewidth=1.50, zorder=-5) 

987 l.set_label(label) 

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

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

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

991 if xlines is not None: 

992 xlines.AppendItem(dat) 

993 

994 self.major_markers.append(l) 

995 if (self.energy_for_zoom is None and 

996 e > view_emin and e < view_emax): 

997 self.energy_for_zoom = e 

998 

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

1000 e = float(eev) * 0.001 

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

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

1003 linewidth=1.25, zorder=-7) 

1004 l.set_label(label) 

1005 

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

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

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

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

1010 

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

1012 if xlines is not None: 

1013 xlines.AppendItem(dat) 

1014 self.minor_markers.append(l) 

1015 

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

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

1018 elif self.hold_lines is not None: 

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

1020 e = float(eev) * 0.001 

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

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

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

1024 l.set_label(label) 

1025 self.hold_markers.append(l) 

1026 

1027 if xlines is not None: 

1028 xlines.Refresh() 

1029 

1030 edge_en = {} 

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

1032 edge_en[edge] = None 

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

1034 if xex is not None: 

1035 en = xex[0]*0.001 

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

1037 edge_en[edge] = en 

1038 out = '' 

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

1040 if edge_en[key] is not None: 

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

1042 if len(out) > 1: 

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

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

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

1046 if edge_en[key] is not None: 

1047 s.append(key) 

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

1049 if len(s) > 0: 

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

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

1052 

1053 self.draw() 

1054 

1055 def onPileupPrediction(self, event=None): 

1056 if event.IsChecked(): 

1057 self.mca.predict_pileup() 

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

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

1060 else: 

1061 self.plotmca(self.mca) 

1062 

1063 def onEscapePrediction(self, event=None): 

1064 if event.IsChecked(): 

1065 self.mca.predict_escape() 

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

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

1068 else: 

1069 self.plotmca(self.mca) 

1070 

1071 

1072 def onYAxis(self, event=None): 

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

1074 ax = self.panel.axes 

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

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

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

1078 ax.yaxis.set_ticks_position('left') 

1079 self.draw() 

1080 

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

1082 try: 

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

1084 except: 

1085 decade = 0 

1086 scale = 10**decade 

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

1088 if abs(decade) < 1.9: 

1089 out = "%.1f" % val 

1090 elif abs(decade) < 3.9: 

1091 out = "%.0f" % val 

1092 return out 

1093 

1094 def onLogLinear(self, event=None): 

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

1096 roiname = None 

1097 if self.selected_roi is not None: 

1098 roiname = self.selected_roi.name 

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

1100 if self.selected_elem is not None: 

1101 self.onShowLines(elem=self.selected_elem) 

1102 if roiname is not None: 

1103 self.onROI(label=roiname) 

1104 if self.y2data is not None: 

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

1106 

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

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

1109 if as_mca2: 

1110 self.mca2 = mca 

1111 kws['new'] = False 

1112 else: 

1113 self.mca = mca 

1114 self.panel.conf.show_grid = False 

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

1116 

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

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

1119 else: 

1120 self.xview_range = xview_range 

1121 

1122 atitles = [] 

1123 if self.mca is not None: 

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

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

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

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

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

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

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

1131 try: 

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

1133 except ValueError: 

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

1135 atitles.append(rtime_str) 

1136 

1137 try: 

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

1139 mca=self.mca, **kws) 

1140 except ValueError: 

1141 pass 

1142 if as_mca2: 

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

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

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

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

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

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

1149 

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

1151 mca=self.mca2, **kws) 

1152 if title is None: 

1153 title = ' '.join(atitles) 

1154 if set_title: 

1155 self.SetTitle(title) 

1156 

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

1158 if mca is not None: 

1159 self.mca = mca 

1160 mca = self.mca 

1161 panel = self.panel 

1162 

1163 panel.yformatter = self._formaty 

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

1165 kwargs = {'xmin': 0, 

1166 'linewidth': 2.5, 

1167 'delay_draw': True, 

1168 'grid': panel.conf.show_grid, 

1169 'ylog_scale': self.ylog_scale, 

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

1171 'axes_style': 'bottom', 

1172 'color': self.conf.spectra_color} 

1173 kwargs.update(kws) 

1174 

1175 self.xdata = 1.0*x[:] 

1176 self.ydata = 1.0*y[:] 

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

1178 ydat = self.ydata 

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

1180 kwargs['ymin'] = 0.9 

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

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

1183 if self.xview_range is not None: 

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

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

1186 

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

1188 if with_rois and mca is not None: 

1189 if not self.rois_shown: 

1190 self.set_roilist(mca=mca) 

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

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

1193 for r in mca.rois: 

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

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

1196 continue 

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

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

1199 if yroi.max() > 0: 

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

1201 panel.oplot(x, yroi, label='rois', **kwargs) 

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

1203 panel.set_viewlimits() 

1204 panel.set_logscale(yscale=yscale) 

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

1206 panel.cursor_mode = 'zoom' 

1207 self.draw() 

1208 panel.canvas.Refresh() 

1209 

1210 

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

1212 is_mca2=False, draw=True): 

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

1214 mca = self.mca 

1215 ix = 0 

1216 if is_mca2: 

1217 mca = self.mca2 

1218 ix = 2 

1219 mca.counts = 1.0*counts[:] 

1220 if energy is not None: 

1221 mca.energy = 1.0*energy[:] 

1222 xnpts = 1.0/len(energy) 

1223 nrois = len(mca.rois) 

1224 if not is_mca2 and with_rois and nrois > 0: 

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

1226 for r in mca.rois: 

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

1228 continue 

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

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

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

1232 update_limits=False) 

1233 

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

1235 draw=False, update_limits=False) 

1236 

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

1238 try: 

1239 max_counts2 = max(self.mca2.counts) 

1240 except: 

1241 pass 

1242 

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

1244 if mca == self.mca: 

1245 self.ydata = 1.0*counts[:] 

1246 self.update_status() 

1247 if draw: self.draw() 

1248 

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

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

1251 if mca is not None: 

1252 self.mca2 = mca 

1253 

1254 self.x2data = 1.0*x[:] 

1255 self.y2data = 1.0*y[:] 

1256 if hasattr(self, 'ydata'): 

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

1258 else: 

1259 ymax = max(y)*1.25 

1260 

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

1262 'ymax' : ymax, 'axes_style': 'bottom', 

1263 'ylog_scale': self.ylog_scale}) 

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

1265 

1266 def swap_mcas(self, event=None): 

1267 if self.mca2 is None: 

1268 return 

1269 self.mca, self.mca2 = self.mca2, self.mca 

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

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

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

1273 setattr(xrfgroup, '_mca2', _mca) 

1274 setattr(xrfgroup, '_mca', _mca2) 

1275 

1276 self.plotmca(self.mca) 

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

1278 

1279 def close_bkg_mca(self, event=None): 

1280 self.mca2 = None 

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

1282 setattr(xrfgroup, '_mca2', '') 

1283 self.plotmca(self.mca) 

1284 

1285 def onReadMCAFile(self, event=None): 

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

1287 defaultDir=get_cwd(), 

1288 wildcard=FILE_WILDCARDS, 

1289 style = wx.FD_OPEN|wx.FD_CHANGE_DIR) 

1290 

1291 filename = None 

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

1293 filename = os.path.abspath(dlg.GetPath()) 

1294 dlg.Destroy() 

1295 

1296 if filename is None: 

1297 return 

1298 if self.mca is not None: 

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

1300 

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

1302 

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

1304 deffile = '' 

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

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

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

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

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

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

1311 if deffile == '': 

1312 deffile ='test' 

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

1314 deffile = deffile + '.mca' 

1315 

1316 _, deffile = os.path.split(deffile) 

1317 deffile = fix_filename(str(deffile)) 

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

1319 default_file=deffile, 

1320 wildcard=FILE_WILDCARDS) 

1321 if outfile is not None: 

1322 self.mca.save_mcafile(outfile) 

1323 

1324 

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

1326 deffile = '' 

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

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

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

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

1331 

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

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

1334 if deffile == '': 

1335 deffile ='test' 

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

1337 deffile = deffile + '.dat' 

1338 

1339 _, deffile = os.path.split(deffile) 

1340 deffile = fix_filename(str(deffile)) 

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

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

1343 default_file=deffile, 

1344 wildcard=ASCII_WILDCARDS) 

1345 if outfile is not None: 

1346 self.mca.save_ascii(outfile) 

1347 

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

1349 try: 

1350 self.win_calib.Raise() 

1351 except: 

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

1353 callback=self.onCalibrationChange) 

1354 

1355 def onCalibrationChange(self, mca): 

1356 """update whenn mca changed calibration""" 

1357 self.plotmca(mca) 

1358 

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

1360 try: 

1361 self.win_fit.Raise() 

1362 except: 

1363 self.win_fit = FitSpectraFrame(self) 

1364 

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

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

1367 self.SetStatusText(s, panel) 

1368 

1369 def showInspectionTool(self, event=None): 

1370 app = wx.GetApp() 

1371 app.ShowInspectionTool() 

1372 

1373 def onAbout(self, event=None): 

1374 dlg = wx.MessageDialog(self, 

1375 """XRF Spectral Viewer 

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

1377 """, 

1378 "About XRF Viewer", 

1379 wx.OK | wx.ICON_INFORMATION) 

1380 dlg.ShowModal() 

1381 dlg.Destroy() 

1382 

1383 def onReadFile(self, event=None): 

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

1385 defaultDir=get_cwd(), 

1386 wildcard=FILE_WILDCARDS, 

1387 style=wx.FD_OPEN) 

1388 path, re1ad = None, False 

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

1390 read = True 

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

1392 if path in self.filemap: 

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

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

1395 dlg.Destroy() 

1396 

1397 if read: 

1398 try: 

1399 parent, fname = os.path.split(path) 

1400 except: 

1401 return 

1402 

1403class XRFApp(LarchWxApp): 

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

1405 self.filename = filename 

1406 LarchWxApp.__init__(self, **kws) 

1407 

1408 def createApp(self): 

1409 frame = XRFDisplayFrame(filename=self.filename) 

1410 frame.Show() 

1411 self.SetTopWindow(frame) 

1412 return True