Coverage for larch/wxlib/specfile_importer.py: 9%

479 statements  

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

1#!/usr/bin/env python 

2""" 

3 

4""" 

5from pathlib import Path 

6 

7import numpy as np 

8np.seterr(all='ignore') 

9 

10from functools import partial 

11 

12import wx 

13import wx.lib.scrolledpanel as scrolled 

14import wx.lib.agw.flatnotebook as fnb 

15from wxmplot import PlotPanel 

16 

17from wxutils import (SimpleText, FloatCtrl, GUIColors, Button, Choice, 

18 FileCheckList, pack, Popup, Check, MenuItem, CEN, 

19 RIGHT, LEFT, FRAMESTYLE, HLine, Font) 

20 

21import larch 

22from larch import Group 

23from larch.utils.strutils import fix_varname 

24from larch.xafs.xafsutils import guess_energy_units 

25from larch.io import look_for_nans, is_specfile, open_specfile 

26from larch.utils.physical_constants import PLANCK_HC, DEG2RAD, PI 

27from .columnframe import MultiColumnFrame, create_arrays, energy_may_need_rebinning 

28 

29CEN |= wx.ALL 

30FNB_STYLE = fnb.FNB_NO_X_BUTTON|fnb.FNB_SMART_TABS 

31FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NODRAG 

32 

33XPRE_OPS = ('', 'log(', '-log(') 

34YPRE_OPS = ('', 'log(', '-log(') 

35ARR_OPS = ('+', '-', '*', '/') 

36 

37YERR_OPS = ('Constant', 'Sqrt(Y)', 'Array') 

38CONV_OPS = ('Lorenztian', 'Gaussian') 

39 

40XDATATYPES = ('xydata', 'xas') 

41ENUNITS_TYPES = ('eV', 'keV', 'degrees', 'not energy') 

42 

43 

44class SpecfileImporter(wx.Frame) : 

45 """Column Data File, select columns""" 

46 def __init__(self, parent, filename=None, config=None, _larch=None, 

47 read_ok_cb=None): 

48 if not is_specfile(filename): 

49 title = "Not a Specfile: %s" % filename 

50 message = "Error reading %s as a Specfile" % filename 

51 r = Popup(parent, message, title) 

52 return None 

53 

54 self.parent = parent 

55 fpath = Path(filename).absolute() 

56 self.path = fpath.as_posix() 

57 self.filename = fpath.name 

58 self._larch = _larch 

59 self.specfile = open_specfile(self.path) 

60 self.scans = [] 

61 curscan = None 

62 for scandata in self.specfile.get_scans(): 

63 name, cmd, dtime = scandata 

64 self.scans.append("%s | %s" % (name, cmd)) 

65 if curscan is None: 

66 curscan = name 

67 

68 self.curscan = self.specfile.get_scan(curscan) 

69 self.subframes = {} 

70 self.workgroup = Group() 

71 for attr in ('path', 'filename', 'datatype', 

72 'array_labels', 'data'): 

73 setattr(self.workgroup, attr, None) 

74 

75 self.array_labels = [l.lower() for l in self.curscan.array_labels] 

76 

77 if self.workgroup.datatype is None: 

78 self.workgroup.datatype = 'xydata' 

79 for arrlab in self.array_labels[:4]: 

80 if 'ener' in arrlab.lower(): 

81 self.workgroup.datatype = 'xas' 

82 

83 self.read_ok_cb = read_ok_cb 

84 

85 self.config = dict(xarr=self.curscan.axis.lower(), yarr1=None, 

86 yarr2=None, yop='/', ypop='', 

87 monod=3.1355316, en_units='eV', 

88 yerror=YERR_OPS[0], yerr_val=1, 

89 yerr_arr=None, dtc_config={}, multicol_config={}) 

90 

91 if config is not None: 

92 self.config.update(config) 

93 

94 if self.config['yarr2'] is None and 'i0' in self.array_labels: 

95 self.config['yarr2'] = 'i0' 

96 

97 if self.config['yarr1'] is None: 

98 if 'itrans' in self.array_labels: 

99 self.config['yarr1'] = 'itrans' 

100 elif 'i1' in self.array_labels: 

101 self.config['yarr1'] = 'i1' 

102 

103 wx.Frame.__init__(self, None, -1, f'Build Arrays for {filename:s}', 

104 style=FRAMESTYLE) 

105 

106 self.SetMinSize((750, 550)) 

107 self.SetSize((850, 650)) 

108 self.colors = GUIColors() 

109 

110 x0, y0 = parent.GetPosition() 

111 self.SetPosition((x0+60, y0+60)) 

112 self.SetFont(Font(10)) 

113 

114 splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) 

115 splitter.SetMinimumPaneSize(200) 

116 

117 leftpanel = wx.Panel(splitter) 

118 ltop = wx.Panel(leftpanel) 

119 

120 sel_none = Button(ltop, 'Select None', size=(100, 30), action=self.onSelNone) 

121 sel_all = Button(ltop, 'Select All', size=(100, 30), action=self.onSelAll) 

122 sel_imp = Button(ltop, 'Import Selected Scans', size=(200, -1), action=self.onOK) 

123 

124 self.scanlist = FileCheckList(leftpanel, select_action=self.onScanSelect) 

125 self.scanlist.AppendItems(self.scans) 

126 

127 tsizer = wx.GridBagSizer(2, 2) 

128 tsizer.Add(sel_all, (0, 0), (1, 1), LEFT, 0) 

129 tsizer.Add(sel_none, (0, 1), (1, 1), LEFT, 0) 

130 tsizer.Add(sel_imp, (1, 0), (1, 2), LEFT, 0) 

131 pack(ltop, tsizer) 

132 

133 sizer = wx.BoxSizer(wx.VERTICAL) 

134 sizer.Add(ltop, 0, LEFT|wx.GROW, 1) 

135 sizer.Add(self.scanlist, 1, LEFT|wx.GROW|wx.ALL, 1) 

136 pack(leftpanel, sizer) 

137 

138 # right hand side 

139 rightpanel = wx.Panel(splitter) 

140 

141 panel = wx.Panel(rightpanel) 

142 # title row 

143 self.title = SimpleText(panel, 

144 " %s, scan %s" % (self.path, self.curscan.scan_name), 

145 font=Font(11), colour=self.colors.title, style=LEFT) 

146 

147 self.wid_scantitle = SimpleText(panel, " %s" % self.curscan.title, 

148 font=Font(11), style=LEFT) 

149 self.wid_scantime = SimpleText(panel, self.curscan.timestring, 

150 font=Font(11), style=LEFT) 

151 

152 yarr_labels = self.yarr_labels = self.array_labels + ['1.0', '0.0', ''] 

153 xarr_labels = self.xarr_labels = self.array_labels + ['_index'] 

154 

155 self.xarr = Choice(panel, choices=xarr_labels, action=self.onXSelect, size=(150, -1)) 

156 self.yarr1 = Choice(panel, choices=yarr_labels, action=self.onUpdate, size=(150, -1)) 

157 self.yarr2 = Choice(panel, choices=yarr_labels, action=self.onUpdate, size=(150, -1)) 

158 self.yerr_arr = Choice(panel, choices=yarr_labels, action=self.onUpdate, size=(150, -1)) 

159 self.yerr_arr.Disable() 

160 

161 self.datatype = Choice(panel, choices=XDATATYPES, action=self.onUpdate, size=(150, -1)) 

162 self.datatype.SetStringSelection(self.workgroup.datatype) 

163 

164 self.en_units = Choice(panel, choices=ENUNITS_TYPES, action=self.onEnUnitsSelect, 

165 size=(150, -1)) 

166 

167 self.ypop = Choice(panel, choices=YPRE_OPS, action=self.onUpdate, size=(150, -1)) 

168 self.yop = Choice(panel, choices=ARR_OPS, action=self.onUpdate, size=(50, -1)) 

169 self.yerr_op = Choice(panel, choices=YERR_OPS, action=self.onYerrChoice, size=(150, -1)) 

170 

171 self.yerr_val = FloatCtrl(panel, value=1, precision=4, size=(90, -1)) 

172 self.monod_val = FloatCtrl(panel, value=3.1355316, precision=7, size=(90, -1)) 

173 

174 xlab = SimpleText(panel, ' X array: ') 

175 ylab = SimpleText(panel, ' Y array: ') 

176 units_lab = SimpleText(panel, ' Units: ') 

177 yerr_lab = SimpleText(panel, ' Yerror: ') 

178 dtype_lab = SimpleText(panel, ' Data Type: ') 

179 monod_lab = SimpleText(panel, ' Mono D spacing (Ang): ') 

180 yerrval_lab = SimpleText(panel, ' Value:') 

181 

182 self.ysuf = SimpleText(panel, '') 

183 self.message = SimpleText(panel, '-', font=Font(11), 

184 colour=self.colors.title, style=LEFT) 

185 

186 self.ypop.SetStringSelection(self.config['ypop']) 

187 self.yop.SetStringSelection(self.config['yop']) 

188 self.monod_val.SetValue(self.config['monod']) 

189 self.monod_val.SetAction(self.onUpdate) 

190 

191 self.monod_val.Enable(self.config['en_units'].startswith('deg')) 

192 self.en_units.SetStringSelection(self.config['en_units']) 

193 self.yerr_op.SetStringSelection(self.config['yerror']) 

194 self.yerr_val.SetValue(self.config['yerr_val']) 

195 if '(' in self.config['ypop']: 

196 self.ysuf.SetLabel(')') 

197 

198 ixsel, iysel, iy2sel, iyesel = 0, 1, len(yarr_labels)-1, len(yarr_labels)-1 

199 if self.config['xarr'] in xarr_labels: 

200 ixsel = xarr_labels.index(self.config['xarr']) 

201 if self.config['yarr1'] in self.array_labels: 

202 iysel = self.array_labels.index(self.config['yarr1']) 

203 if self.config['yarr2'] in yarr_labels: 

204 iy2sel = yarr_labels.index(self.config['yarr2']) 

205 if self.config['yerr_arr'] in yarr_labels: 

206 iyesel = yarr_labels.index(self.config['yerr_arr']) 

207 self.xarr.SetSelection(ixsel) 

208 self.yarr1.SetSelection(iysel) 

209 self.yarr2.SetSelection(iy2sel) 

210 self.yerr_arr.SetSelection(iyesel) 

211 

212 bpanel = wx.Panel(panel) 

213 bsizer = wx.BoxSizer(wx.HORIZONTAL) 

214 self.multi_sel = Button(bpanel, 'Select Multilple Columns', action=self.onMultiColumn) 

215 self.multi_clear = Button(bpanel, 'Clear Multiple Columns', action=self.onClearMultiColumn) 

216 self.multi_clear.Disable() 

217 self.multi_sel.SetToolTip('Select Multiple Columns to import as separate groups') 

218 self.multi_clear.SetToolTip('Clear Multiple Column Selection') 

219 bsizer.Add(self.multi_sel) 

220 bsizer.Add(self.multi_clear) 

221 pack(bpanel, bsizer) 

222 

223 sizer = wx.GridBagSizer(2, 2) 

224 ir = 0 

225 sizer.Add(self.title, (ir, 0), (1, 7), LEFT, 5) 

226 

227 ir += 1 

228 sizer.Add(self.wid_scantitle, (ir, 0), (1, 3), LEFT, 0) 

229 sizer.Add(self.wid_scantime, (ir, 3), (1, 2), LEFT, 0) 

230 

231 

232 ir += 1 

233 sizer.Add(xlab, (ir, 0), (1, 1), LEFT, 0) 

234 sizer.Add(self.xarr, (ir, 1), (1, 1), LEFT, 0) 

235 sizer.Add(units_lab, (ir, 2), (1, 2), RIGHT, 0) 

236 sizer.Add(self.en_units, (ir, 4), (1, 2), LEFT, 0) 

237 

238 ir += 1 

239 sizer.Add(dtype_lab, (ir, 0), (1, 1), LEFT, 0) 

240 sizer.Add(self.datatype, (ir, 1), (1, 1), LEFT, 0) 

241 sizer.Add(monod_lab, (ir, 2), (1, 2), RIGHT, 0) 

242 sizer.Add(self.monod_val, (ir, 4), (1, 1), LEFT, 0) 

243 

244 ir += 1 

245 sizer.Add(ylab, (ir, 0), (1, 1), LEFT, 0) 

246 sizer.Add(self.ypop, (ir, 1), (1, 1), LEFT, 0) 

247 sizer.Add(self.yarr1, (ir, 2), (1, 1), LEFT, 0) 

248 sizer.Add(self.yop, (ir, 3), (1, 1), RIGHT, 0) 

249 sizer.Add(self.yarr2, (ir, 4), (1, 1), LEFT, 0) 

250 sizer.Add(self.ysuf, (ir, 5), (1, 1), LEFT, 0) 

251 

252 ir += 1 

253 sizer.Add(yerr_lab, (ir, 0), (1, 1), LEFT, 0) 

254 sizer.Add(self.yerr_op, (ir, 1), (1, 1), LEFT, 0) 

255 sizer.Add(self.yerr_arr, (ir, 2), (1, 1), LEFT, 0) 

256 sizer.Add(yerrval_lab, (ir, 3), (1, 1), RIGHT, 0) 

257 sizer.Add(self.yerr_val, (ir, 4), (1, 2), LEFT, 0) 

258 

259 ir += 1 

260 sizer.Add(self.message, (ir, 0), (1, 4), LEFT, 0) 

261 ir +=1 

262 sizer.Add(bpanel, (ir, 0), (1, 5), LEFT, 3) 

263 

264 pack(panel, sizer) 

265 

266 self.nb = fnb.FlatNotebook(rightpanel, -1, agwStyle=FNB_STYLE) 

267 self.nb.SetTabAreaColour(wx.Colour(248,248,240)) 

268 self.nb.SetActiveTabColour(wx.Colour(254,254,195)) 

269 self.nb.SetNonActiveTabTextColour(wx.Colour(40,40,180)) 

270 self.nb.SetActiveTabTextColour(wx.Colour(80,0,0)) 

271 

272 self.plotpanel = PlotPanel(rightpanel, messenger=self.plot_messages) 

273 try: 

274 plotopts = self._larch.symtable._sys.wx.plotopts 

275 self.plotpanel.conf.set_theme(plotopts['theme']) 

276 self.plotpanel.conf.enable_grid(plotopts['show_grid']) 

277 except: 

278 pass 

279 

280 self.plotpanel.SetMinSize((300, 250)) 

281 

282 shead = wx.Panel(rightpanel) 

283 self.scanheader = wx.TextCtrl(shead, style=wx.TE_MULTILINE|wx.TE_READONLY, 

284 size=(400, 250)) 

285 self.scanheader.SetValue('\n'.join(self.curscan.scan_header)) 

286 self.scanheader.SetFont(Font(10)) 

287 textsizer = wx.BoxSizer(wx.VERTICAL) 

288 textsizer.Add(self.scanheader, 1, LEFT|wx.GROW, 1) 

289 pack(shead, textsizer) 

290 

291 

292 fhead = wx.Panel(rightpanel) 

293 self.fileheader = wx.TextCtrl(fhead, style=wx.TE_MULTILINE|wx.TE_READONLY, 

294 size=(400, 250)) 

295 self.fileheader.SetValue('\n'.join(self.curscan.file_header)) 

296 self.fileheader.SetFont(Font(10)) 

297 textsizer = wx.BoxSizer(wx.VERTICAL) 

298 textsizer.Add(self.fileheader, 1, LEFT|wx.GROW, 1) 

299 pack(fhead, textsizer) 

300 

301 

302 

303 self.nb.AddPage(fhead, ' File Header ', True) 

304 self.nb.AddPage(shead, ' Scan Header ', True) 

305 self.nb.AddPage(self.plotpanel, ' Plot of Selected Arrays ', True) 

306 

307 sizer = wx.BoxSizer(wx.VERTICAL) 

308 sizer.Add(panel, 0, LEFT|wx.GROW, 1) 

309 sizer.Add(self.nb, 1, LEFT|wx.GROW|wx.ALL, 1) 

310 pack(rightpanel, sizer) 

311 

312 splitter.SplitVertically(leftpanel, rightpanel, 1) 

313 self.statusbar = self.CreateStatusBar(2, 0) 

314 self.statusbar.SetStatusWidths([-1, -1]) 

315 statusbar_fields = [filename, ""] 

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

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

318 

319 self.set_energy_units() 

320 csize = self.GetSize() 

321 bsize = self.GetBestSize() 

322 if bsize[0] > csize[0]: csize[0] = bsize[0] 

323 if bsize[1] > csize[1]: csize[1] = bsize[1] 

324 self.SetSize(csize) 

325 self.Show() 

326 self.Raise() 

327 self.onUpdate(self) 

328 

329 def set_energy_units(self): 

330 ix = self.xarr.GetSelection() 

331 xname = self.xarr.GetStringSelection() 

332 rdata = self.curscan.data 

333 try: 

334 ncol, npts = rdata.shape 

335 except: 

336 self.statusbar.SetStatusText(f"Warning: Could not read data for scan '{self.curscan.title:s}'") 

337 

338 workgroup = self.workgroup 

339 if xname.startswith('_index') or ix >= ncol: 

340 workgroup.xplot = 1.0*np.arange(npts) 

341 else: 

342 workgroup.xplot = 1.0*rdata[ix, :] 

343 eguess = guess_energy_units(workgroup.xplot) 

344 if eguess.startswith('eV'): 

345 self.en_units.SetStringSelection('eV') 

346 elif eguess.startswith('keV'): 

347 self.en_units.SetStringSelection('keV') 

348 

349 def onScanSelect(self, event=None): 

350 try: 

351 scan_desc = event.GetString() 

352 name = [s.strip() for s in scan_desc.split(' | ')][0] 

353 self.curscan = self.specfile.get_scan(name) 

354 except: 

355 return 

356 slist = list(self.scanlist.GetCheckedStrings()) 

357 if scan_desc not in slist: 

358 slist.append(scan_desc) 

359 self.scanlist.SetCheckedStrings(slist) 

360 

361 self.wid_scantitle.SetLabel(" %s" % self.curscan.title) 

362 self.wid_scantime.SetLabel(self.curscan.timestring) 

363 

364 self.title.SetLabel(" %s, scan %s" % (self.path, self.curscan.scan_name)) 

365 self.array_labels = [l.lower() for l in self.curscan.array_labels] 

366 self.workgroup.array_labels = self.array_labels 

367 self.workgroup.data = self.curscan.data 

368 

369 yarr_labels = self.yarr_labels = self.array_labels + ['1.0', '0.0', ''] 

370 xarr_labels = self.xarr_labels = self.array_labels + ['_index'] 

371 

372 xsel = self.xarr.GetStringSelection() 

373 self.xarr.Clear() 

374 self.xarr.AppendItems(xarr_labels) 

375 if xsel in xarr_labels: 

376 self.xarr.SetStringSelection(xsel) 

377 else: 

378 self.xarr.SetSelection(0) 

379 

380 y1sel = self.yarr1.GetStringSelection() 

381 self.yarr1.Clear() 

382 self.yarr1.AppendItems(yarr_labels) 

383 if y1sel in yarr_labels: 

384 self.yarr1.SetStringSelection(y1sel) 

385 else: 

386 self.yarr1.SetSelection(1) 

387 

388 y2sel = self.yarr2.GetStringSelection() 

389 self.yarr2.Clear() 

390 self.yarr2.AppendItems(yarr_labels) 

391 if y2sel in yarr_labels: 

392 self.yarr2.SetStringSelection(y2sel) 

393 

394 xsel = self.xarr.GetStringSelection() 

395 self.workgroup.datatype = 'xas' if 'en' in xsel else 'xydata' 

396 self.datatype.SetStringSelection(self.workgroup.datatype) 

397 

398 self.scanheader.SetValue('\n'.join(self.curscan.scan_header)) 

399 self.set_energy_units() 

400 self.onUpdate() 

401 

402 def onClearMultiColumn(self, event=None): 

403 self.config['multicol_config'] = {} 

404 self.message.SetLabel(f" cleared reading of multiple columns") 

405 self.multi_clear.Disable() 

406 self.yarr1.Enable() 

407 self.ypop.Enable() 

408 self.yop.Enable() 

409 self.onUpdate() 

410 

411 def onMultiColumn(self, event=None): 

412 if 'multicol_config' not in self.config: 

413 self.config['multicol_config'] = {} 

414 

415 if len(self.array_labels) < 1: 

416 self.array_labels = [l.lower() for l in self.curscan.array_labels] 

417 self.workgroup.array_labels = self.array_labels 

418 self.workgroup.data = self.curscan.data 

419 self.show_subframe('multicol', MultiColumnFrame, 

420 config=self.config['multicol_config'], 

421 group=self.workgroup, 

422 on_ok=self.onMultiColumn_OK) 

423 

424 def onMultiColumn_OK(self, config, update=True, **kws): 

425 chans = config.get('channels', []) 

426 if len(chans) == 0: 

427 self.config['multicol_config'] = {} 

428 else: 

429 self.config['multicol_config'] = config 

430 self.yarr1.SetSelection(chans[0]) 

431 self.yarr2.SetSelection(config['i0']) 

432 self.ypop.SetStringSelection('') 

433 self.yarr1.Disable() 

434 self.ypop.Disable() 

435 self.yop.Disable() 

436 y2 = self.yarr2.GetStringSelection() 

437 msg = f" Will import {len(config['channels'])} Y arrays, divided by '{y2}'" 

438 self.message.SetLabel(msg) 

439 self.multi_clear.Enable() 

440 if update: 

441 self.onUpdate() 

442 

443 

444 def show_subframe(self, name, frameclass, **opts): 

445 shown = False 

446 if name in self.subframes: 

447 try: 

448 self.subframes[name].Raise() 

449 shown = True 

450 except: 

451 pass 

452 if not shown: 

453 self.subframes[name] = frameclass(self, **opts) 

454 self.subframes[name].Show() 

455 self.subframes[name].Raise() 

456 

457 def set_array_labels(self, arr_labels): 

458 self.workgroup.array_labels = arr_labels 

459 yarr_labels = self.yarr_labels = arr_labels + ['1.0', '0.0', ''] 

460 xarr_labels = self.xarr_labels = arr_labels + ['_index'] 

461 def update(wid, choices): 

462 curstr = wid.GetStringSelection() 

463 curind = wid.GetSelection() 

464 wid.SetChoices(choices) 

465 if curstr in choices: 

466 wid.SetStringSelection(curstr) 

467 else: 

468 wid.SetSelection(curind) 

469 update(self.xarr, xarr_labels) 

470 update(self.yarr1, yarr_labels) 

471 update(self.yarr2, yarr_labels) 

472 update(self.yerr_arr, yarr_labels) 

473 self.onUpdate() 

474 

475 def onSelAll(self, event=None): 

476 self.scanlist.SetCheckedStrings(self.scans) 

477 

478 def onSelNone(self, event=None): 

479 self.scanlist.SetCheckedStrings([]) 

480 

481 def onOK(self, event=None): 

482 """ build arrays according to selection """ 

483 

484 scanlist = [] 

485 for s in self.scanlist.GetCheckedStrings(): 

486 words = [s.strip() for s in s.split('|')] 

487 scanlist.append(words[0]) 

488 if len(scanlist) == 0: 

489 cancel = Popup(self, """No scans selected. 

490 Cancel import from this project?""", 'Cancel Import?', 

491 style=wx.YES_NO) 

492 if wx.ID_YES == cancel: 

493 self.Destroy() 

494 else: 

495 return 

496 

497 self.read_form() 

498 cout = create_arrays(self.workgroup, **self.config) 

499 self.config.update(cout) 

500 conf = self.config 

501 conf['array_labels'] = self.workgroup.array_labels 

502 

503 if self.ypop.Enabled: #not using multicolumn mode 

504 conf['multicol_config'] = {'channels': [], 'i0': conf['iy2']} 

505 

506 self.expressions = conf['expressions'] 

507 

508 # generate script to pass back to calling program: 

509 # read_cmd = "_specfile.get_scan(scan='{scan}')" 

510 buff = ["{group} = {specfile}.get_scan(scan='{scan}')", 

511 "{group}.path = '{path}'", 

512 "{group}.is_frozen = False"] 

513 

514 for attr in ('datatype', 'plot_xlabel', 'plot_ylabel'): 

515 val = getattr(self.workgroup, attr) 

516 buff.append("{group}.%s = '%s'" % (attr, val)) 

517 

518 xexpr = self.expressions['xplot'] 

519 en_units = conf['en_units'] 

520 if en_units.startswith('deg'): 

521 buff.append(f"mono_dspace = {dspace:.9f}") 

522 buff.append(f"{ group} .xplot = PLANCK_HC/(2*mono_dspace*sin(DEG2RAD*({expr:s})))") 

523 elif en_units.startswith('keV'): 

524 buff.append(f"{ group} .xplot = 1000.0*{xexpr:s}") 

525 else: 

526 buff.append(f"{ group} .xplot = {xexpr:s}") 

527 

528 for aname in ('yplot', 'yerr'): 

529 expr = self.expressions[aname] 

530 buff.append(f"{ group} .{aname} = {expr}") 

531 

532 dtype = getattr(self.workgroup, 'datatype', 'xytype') 

533 if dtype == 'xas': 

534 buff.append("{group}.energy = {group}.xplot"[:]) 

535 buff.append("{group}.mu = {group}.yplot[:]") 

536 buff.append("sort_xafs({group}, overwrite=True, fix_repeats=True)") 

537 elif dtype == 'xydata': 

538 buff.append("{group}.x = {group}.xplot[:]") 

539 buff.append("{group}.y = {group}.yplot[:]") 

540 buff.append("{group}.scale = (ptp({group}.yplot)+1.e-15)") 

541 buff.append("{group}.xshift = 0.0") 

542 

543 script = "\n".join(buff) 

544 

545 self.config['array_desc'] = dict(xplot=self.workgroup.plot_xlabel, 

546 yplot=self.workgroup.plot_ylabel, 

547 yerr=self.expressions['yerr']) 

548 if self.read_ok_cb is not None: 

549 self.read_ok_cb(script, self.path, scanlist, 

550 config=self.config) 

551 

552 for f in self.subframes.values(): 

553 try: 

554 f.Destroy() 

555 except: 

556 pass 

557 self.Destroy() 

558 

559 def onCancel(self, event=None): 

560 self.workgroup.import_ok = False 

561 for f in self.subframes.values(): 

562 try: 

563 f.Destroy() 

564 except: 

565 pass 

566 self.Destroy() 

567 

568 def onYerrChoice(self, evt=None): 

569 yerr_choice = evt.GetString() 

570 self.yerr_arr.Disable() 

571 self.yerr_val.Disable() 

572 if 'const' in yerr_choice.lower(): 

573 self.yerr_val.Enable() 

574 elif 'array' in yerr_choice.lower(): 

575 self.yerr_arr.Enable() 

576 self.onUpdate() 

577 

578 def onXSelect(self, evt=None): 

579 ix = self.xarr.GetSelection() 

580 xname = self.xarr.GetStringSelection() 

581 

582 workgroup = self.workgroup 

583 rdata = self.curscan.data 

584 ncol, npts = rdata.shape 

585 if xname.startswith('_index') or ix >= ncol: 

586 workgroup.xplot = 1.0*np.arange(npts) 

587 else: 

588 workgroup.xplot = 1.0*rdata[ix, :] 

589 

590 self.monod_val.Disable() 

591 if self.datatype.GetStringSelection().strip().lower() == 'xydata': 

592 self.en_units.SetSelection(4) 

593 else: 

594 eguess = guess_energy_units(workgroup.xplot) 

595 if eguess.startswith('keV'): 

596 self.en_units.SetSelection(1) 

597 elif eguess.startswith('deg'): 

598 self.en_units.SetSelection(2) 

599 self.monod_val.Enable() 

600 else: 

601 self.en_units.SetSelection(0) 

602 

603 self.onUpdate() 

604 

605 def onEnUnitsSelect(self, evt=None): 

606 self.monod_val.Enable(self.en_units.GetStringSelection().startswith('deg')) 

607 self.onUpdate() 

608 

609 def read_form(self, **kws): 

610 """return form configuration""" 

611 datatype = self.datatype.GetStringSelection().strip().lower() 

612 if datatype == 'xydata': 

613 self.en_units.SetStringSelection('not energy') 

614 

615 conf = {'datatype': datatype, 

616 'ix': self.xarr.GetSelection(), 

617 'xarr': self.xarr.GetStringSelection(), 

618 'en_units': self.en_units.GetStringSelection(), 

619 'monod': float(self.monod_val.GetValue()), 

620 'yarr1': self.yarr1.GetStringSelection().strip(), 

621 'yarr2': self.yarr2.GetStringSelection().strip(), 

622 'iy1': self.yarr1.GetSelection(), 

623 'iy2': self.yarr2.GetSelection(), 

624 'yop': self.yop.GetStringSelection().strip(), 

625 'ypop': self.ypop.GetStringSelection().strip(), 

626 'iyerr': self.yerr_arr.GetSelection(), 

627 'yerr_arr': self.yerr_arr.GetStringSelection(), 

628 'yerr_op': self.yerr_op.GetStringSelection().lower(), 

629 'yerr_val': self.yerr_val.GetValue(), 

630 } 

631 self.config.update(conf) 

632 return conf 

633 

634 

635 def onUpdate(self, value=None, evt=None): 

636 """column selections changed calc xplot and yplot""" 

637 workgroup = self.workgroup 

638 workgroup.data = self.curscan.data 

639 workgroup.filename = self.curscan.filename 

640 

641 conf = self.read_form() 

642 cout = create_arrays(workgroup, **conf) 

643 

644 self.expression = cout.pop('expressions') 

645 conf.update(cout) 

646 

647 if energy_may_need_rebinning(workgroup): 

648 self.message.SetLabel("Warning: XAS data may need to be rebinned!") 

649 

650 popts = dict(marker='o', markersize=4, linewidth=1.5, 

651 title=Path(workgroup.filename).name, 

652 ylabel=workgroup.plot_ylabel, 

653 xlabel=workgroup.plot_xlabel, 

654 label=workgroup.plot_ylabel) 

655 try: 

656 self.plotpanel.plot(workgroup.xplot, workgroup.yplot, **popts) 

657 except: 

658 pass 

659 

660 

661 def plot_messages(self, msg, panel=1): 

662 self.statusbar.SetStatusText(msg, panel)