Coverage for larch/wxlib/xas_importer.py: 8%

750 statements  

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

1import re 

2import numpy as np 

3from functools import partial 

4from pathlib import Path 

5import wx 

6import wx.lib.scrolledpanel as scrolled 

7import wx.lib.agw.flatnotebook as fnb 

8from wxmplot import PlotPanel 

9 

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

11 FileCheckList, pack, Popup, Check, MenuItem, CEN, RIGHT, LEFT, 

12 FRAMESTYLE, HLine, Font) 

13 

14import larch 

15from larch import Group 

16from larch.utils.strutils import fix_varname 

17from larch.xafs.xafsutils import guess_energy_units 

18from larch.io.xas_data_source import open_xas_source 

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

20 

21CEN |= wx.ALL 

22FNB_STYLE = fnb.FNB_NO_X_BUTTON | fnb.FNB_SMART_TABS 

23FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NODRAG 

24 

25XPRE_OPS = ("", "log(", "-log(") 

26YPRE_OPS = ("", "log(", "-log(") 

27ARR_OPS = ("+", "-", "*", "/") 

28 

29YERR_OPS = ("Constant", "Sqrt(Y)", "Array") 

30CONV_OPS = ("Lorenztian", "Gaussian") 

31 

32XDATATYPES = ("xydata", "xas") 

33ENUNITS_TYPES = ("eV", "keV", "degrees", "not energy") 

34 

35 

36class AddColumnsFrame(wx.Frame): 

37 """Add Column Labels for a larch grouop""" 

38 

39 def __init__(self, parent, group, on_ok=None): 

40 self.parent = parent 

41 self.group = group 

42 self.on_ok = on_ok 

43 super().__init__( 

44 None, 

45 -1, 

46 "Add Selected Columns", 

47 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, 

48 ) 

49 

50 self.SetFont(Font(10)) 

51 sizer = wx.GridBagSizer(2, 2) 

52 panel = scrolled.ScrolledPanel(self) 

53 

54 self.SetMinSize((550, 550)) 

55 

56 self.wids = {} 

57 

58 lab_aname = SimpleText(panel, label=" Save Array Name:") 

59 lab_range = SimpleText(panel, label=" Use column index:") 

60 lab_regex = SimpleText(panel, label=" Use column label:") 

61 

62 wids = self.wids = {} 

63 

64 wids["arrayname"] = wx.TextCtrl(panel, value="sum", size=(175, -1)) 

65 wids["tc_nums"] = wx.TextCtrl(panel, value="1,3-10", size=(175, -1)) 

66 wids["tc_regex"] = wx.TextCtrl(panel, value="*fe*", size=(175, -1)) 

67 

68 savebtn = Button(panel, "Save", action=self.onOK) 

69 plotbtn = Button(panel, "Plot Sum", action=self.onPlot) 

70 sel_nums = Button(panel, "Select by Index", action=self.onSelColumns) 

71 sel_re = Button(panel, "Select by Pattern", action=self.onSelRegex) 

72 

73 sizer.Add(lab_aname, (0, 0), (1, 2), LEFT, 3) 

74 sizer.Add(wids["arrayname"], (0, 2), (1, 1), LEFT, 3) 

75 

76 sizer.Add(plotbtn, (0, 3), (1, 1), LEFT, 3) 

77 sizer.Add(savebtn, (0, 4), (1, 1), LEFT, 3) 

78 

79 sizer.Add(lab_range, (1, 0), (1, 2), LEFT, 3) 

80 sizer.Add(wids["tc_nums"], (1, 2), (1, 1), LEFT, 3) 

81 sizer.Add(sel_nums, (1, 3), (1, 2), LEFT, 3) 

82 

83 sizer.Add(lab_regex, (2, 0), (1, 2), LEFT, 3) 

84 sizer.Add(wids["tc_regex"], (2, 2), (1, 1), LEFT, 3) 

85 sizer.Add(sel_re, (2, 3), (1, 2), LEFT, 3) 

86 

87 sizer.Add(HLine(panel, size=(550, 2)), (3, 0), (1, 5), LEFT, 3) 

88 ir = 4 

89 

90 cind = SimpleText(panel, label=" Index ") 

91 csel = SimpleText(panel, label=" Select ") 

92 cname = SimpleText(panel, label=" Array Name ") 

93 

94 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3) 

95 sizer.Add(csel, (ir, 1), (1, 1), LEFT, 3) 

96 sizer.Add(cname, (ir, 2), (1, 3), LEFT, 3) 

97 

98 for i, name in enumerate(group.array_labels): 

99 ir += 1 

100 cind = SimpleText(panel, label=" %i " % (i + 1)) 

101 cname = SimpleText(panel, label=" %s " % name) 

102 csel = Check(panel, label="", default=False) 

103 

104 self.wids["col_%d" % i] = csel 

105 

106 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3) 

107 sizer.Add(csel, (ir, 1), (1, 1), LEFT, 3) 

108 sizer.Add(cname, (ir, 2), (1, 3), LEFT, 3) 

109 

110 pack(panel, sizer) 

111 panel.SetupScrolling() 

112 

113 mainsizer = wx.BoxSizer(wx.VERTICAL) 

114 mainsizer.Add(panel, 1, wx.GROW | wx.ALL, 1) 

115 

116 pack(self, mainsizer) 

117 self.Show() 

118 self.SetSize(self.GetBestSize()) 

119 self.Raise() 

120 

121 def make_sum(self): 

122 sel = [] 

123 for name, wid in self.wids.items(): 

124 if name.startswith("col_") and wid.IsChecked(): 

125 sel.append(int(name[4:])) 

126 self.selected_columns = np.array(sel) 

127 narr, npts = self.group.raw.data.shape 

128 yplot = np.zeros(npts, dtype=np.float) 

129 for i in sel: 

130 yplot += self.group.raw.data[i, :] 

131 return yplot 

132 

133 def get_label(self): 

134 label_in = self.wids["arrayname"].GetValue() 

135 label = fix_varname(label_in) 

136 if label in self.group.array_labels: 

137 count = 1 

138 while label in self.group.array_labels and count < 1000: 

139 label = "%s_%d" % (label, count) 

140 count += 1 

141 if label != label_in: 

142 self.wids["arrayname"].SetValue(label) 

143 return label 

144 

145 def onOK(self, event=None): 

146 yplot = self.make_sum() 

147 npts = len(yplot) 

148 label = self.get_label() 

149 self.group.array_labels.append(label) 

150 new = np.append(self.group.raw.data, yplot.reshape(1, npts), axis=0) 

151 self.group.raw.data = new 

152 self.on_ok(label, self.selected_columns) 

153 

154 def onPlot(self, event=None): 

155 yplot = self.make_sum() 

156 xplot = self.group.xplot 

157 label = self.get_label() 

158 label = "%s (not saved)" % label 

159 popts = dict( 

160 marker="o", 

161 markersize=4, 

162 linewidth=1.5, 

163 ylabel=label, 

164 label=label, 

165 xlabel=self.group.plot_xlabel, 

166 ) 

167 self.parent.plotpanel.plot(xplot, yplot, **popts) 

168 

169 def onSelColumns(self, event=None): 

170 pattern = self.wids["tc_nums"].GetValue().split(",") 

171 sel = [] 

172 for part in pattern: 

173 if "-" in part: 

174 start, stop = part.split("-") 

175 try: 

176 istart = int(start) 

177 except ValueError: 

178 istart = 1 

179 try: 

180 istop = int(stop) 

181 except ValueError: 

182 istop = len(self.group.array_labels) + 1 

183 

184 sel.extend(range(istart - 1, istop)) 

185 else: 

186 try: 

187 sel.append(int(part) - 1) 

188 except Exception: 

189 pass 

190 

191 for name, wid in self.wids.items(): 

192 if name.startswith("col_"): 

193 wid.SetValue(int(name[4:]) in sel) 

194 

195 def onSelRegex(self, event=None): 

196 pattern = self.wids["tc_regex"].GetValue().replace("*", ".*") 

197 pattern = pattern.replace("..*", ".*") 

198 sel = [] 

199 for i, name in enumerate(self.group.array_labels): 

200 sel = re.search(pattern, name, flags=re.IGNORECASE) is not None 

201 self.wids["col_%d" % i].SetValue(sel) 

202 

203 

204class EditColumnFrame(wx.Frame): 

205 """Edit Column Labels for a larch grouop""" 

206 

207 def __init__(self, parent, group, on_ok=None): 

208 self.parent = parent 

209 self.group = group 

210 self.on_ok = on_ok 

211 super().__init__( 

212 None, 

213 -1, 

214 "Edit Array Names", 

215 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, 

216 ) 

217 

218 self.SetFont(Font(10)) 

219 sizer = wx.GridBagSizer(2, 2) 

220 panel = scrolled.ScrolledPanel(self) 

221 

222 self.SetMinSize((675, 450)) 

223 

224 self.wids = {} 

225 ir = 0 

226 sizer.Add( 

227 Button(panel, "Apply Changes", size=(200, -1), action=self.onOK), 

228 (0, 1), 

229 (1, 2), 

230 LEFT, 

231 3, 

232 ) 

233 sizer.Add( 

234 Button(panel, "Use Column Number", size=(200, -1), action=self.onColNumber), 

235 (0, 3), 

236 (1, 2), 

237 LEFT, 

238 3, 

239 ) 

240 sizer.Add(HLine(panel, size=(550, 2)), (1, 1), (1, 5), LEFT, 3) 

241 

242 cind = SimpleText(panel, label="Column") 

243 cold = SimpleText(panel, label="Current Name") 

244 cnew = SimpleText(panel, label="Enter New Name") 

245 cret = SimpleText(panel, label=" Result ", size=(150, -1)) 

246 cinfo = SimpleText(panel, label=" Data Range") 

247 cplot = SimpleText(panel, label=" Plot") 

248 

249 ir = 2 

250 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3) 

251 sizer.Add(cold, (ir, 1), (1, 1), LEFT, 3) 

252 sizer.Add(cnew, (ir, 2), (1, 1), LEFT, 3) 

253 sizer.Add(cret, (ir, 3), (1, 1), LEFT, 3) 

254 sizer.Add(cinfo, (ir, 4), (1, 1), LEFT, 3) 

255 sizer.Add(cplot, (ir, 5), (1, 1), LEFT, 3) 

256 

257 for i, name in enumerate(group.array_labels): 

258 ir += 1 

259 cind = SimpleText(panel, label=" %i " % (i + 1)) 

260 cold = SimpleText(panel, label=" %s " % name) 

261 cret = SimpleText(panel, label=fix_varname(name), size=(150, -1)) 

262 

263 cnew = wx.TextCtrl( 

264 panel, value=name, size=(150, -1), style=wx.TE_PROCESS_ENTER 

265 ) 

266 

267 cnew.Bind(wx.EVT_TEXT_ENTER, partial(self.update, index=i)) 

268 cnew.Bind(wx.EVT_KILL_FOCUS, partial(self.update, index=i)) 

269 

270 arr = group.data[i, :] 

271 info_str = " [ %8g : %8g ] " % (arr.min(), arr.max()) 

272 cinfo = SimpleText(panel, label=info_str) 

273 cplot = Button(panel, "Plot", action=partial(self.onPlot, index=i)) 

274 

275 self.wids["%d" % i] = cnew 

276 self.wids["ret_%d" % i] = cret 

277 

278 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3) 

279 sizer.Add(cold, (ir, 1), (1, 1), LEFT, 3) 

280 sizer.Add(cnew, (ir, 2), (1, 1), LEFT, 3) 

281 sizer.Add(cret, (ir, 3), (1, 1), LEFT, 3) 

282 sizer.Add(cinfo, (ir, 4), (1, 1), LEFT, 3) 

283 sizer.Add(cplot, (ir, 5), (1, 1), LEFT, 3) 

284 

285 pack(panel, sizer) 

286 panel.SetupScrolling() 

287 

288 mainsizer = wx.BoxSizer(wx.VERTICAL) 

289 mainsizer.Add(panel, 1, wx.GROW | wx.ALL, 1) 

290 

291 pack(self, mainsizer) 

292 self.Show() 

293 self.Raise() 

294 

295 def onPlot(self, event=None, index=None): 

296 if index is not None: 

297 x = self.parent.workgroup.index 

298 y = self.parent.workgroup.data[index, :] 

299 label = self.wids["ret_%i" % index].GetLabel() 

300 popts = dict( 

301 marker="o", 

302 markersize=4, 

303 linewidth=1.5, 

304 ylabel=label, 

305 xlabel="data point", 

306 label=label, 

307 ) 

308 self.parent.plotpanel.plot(x, y, **popts) 

309 

310 def onColNumber(self, evt=None, index=-1): 

311 for name, wid in self.wids.items(): 

312 val = name 

313 if name.startswith("ret_"): 

314 val = name[4:] 

315 setter = wid.SetLabel 

316 else: 

317 setter = wid.SetValue 

318 setter("col_%d" % (int(val) + 1)) 

319 

320 def update(self, evt=None, index=-1): 

321 newval = fix_varname(self.wids["%d" % index].GetValue()) 

322 self.wids["ret_%i" % index].SetLabel(newval) 

323 

324 def onOK(self, evt=None): 

325 group = self.group 

326 array_labels = [] 

327 for i in range(len(self.group.array_labels)): 

328 newname = self.wids["ret_%i" % i].GetLabel() 

329 array_labels.append(newname) 

330 

331 if callable(self.on_ok): 

332 self.on_ok(array_labels) 

333 self.Destroy() 

334 

335 

336class XasImporter(wx.Frame): 

337 """Column Data File, select columns""" 

338 

339 def __init__( 

340 self, 

341 parent, 

342 filename=None, 

343 data_source=None, 

344 last_array_sel=None, 

345 _larch=None, 

346 read_ok_cb=None, 

347 ): 

348 if data_source is None: 

349 try: 

350 data_source = open_xas_source(filename) 

351 except Exception as e: 

352 title = "Not a valid data source: %s" % filename 

353 message = "Data source error %s: %s" % (filename, e) 

354 r = Popup(parent, message, title) 

355 return None 

356 self.data_source = data_source 

357 

358 self.parent = parent 

359 self.path = filename 

360 self.extra_sums = {} 

361 self._larch = _larch 

362 self.scans = self.data_source.get_sorted_scan_names() 

363 self.curscan = None 

364 

365 self.subframes = {} 

366 self.workgroup = Group() 

367 for attr in ("path", "filename", "datatype", "array_labels", "data"): 

368 setattr(self.workgroup, attr, None) 

369 

370 self.workgroup.datatype = "xas" 

371 

372 self.read_ok_cb = read_ok_cb 

373 

374 self.array_sel = dict( 

375 xarr=None, 

376 yarr1=None, 

377 yarr2=None, 

378 yop="/", 

379 ypop="", 

380 monod=3.1355316, 

381 en_units="eV", 

382 yerror=YERR_OPS[0], 

383 yerr_val=1, 

384 yerr_arr=None, 

385 ) 

386 

387 if last_array_sel is not None: 

388 self.array_sel.update(last_array_sel) 

389 

390 super().__init__(None, -1, f"Build Arrays for {filename:s}", style=FRAMESTYLE) 

391 

392 self.SetMinSize((750, 550)) 

393 self.SetSize((850, 650)) 

394 self.colors = GUIColors() 

395 

396 x0, y0 = parent.GetPosition() 

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

398 self.SetFont(Font(10)) 

399 

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

401 splitter.SetMinimumPaneSize(200) 

402 

403 leftpanel = wx.Panel(splitter) 

404 ltop = wx.Panel(leftpanel) 

405 

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

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

408 sel_imp = Button( 

409 ltop, "Import Selected Scans", size=(200, -1), action=self.onOK 

410 ) 

411 

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

413 self.scanlist.AppendItems(self.scans) 

414 

415 tsizer = wx.GridBagSizer(2, 2) 

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

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

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

419 pack(ltop, tsizer) 

420 

421 sizer = wx.BoxSizer(wx.VERTICAL) 

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

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

424 pack(leftpanel, sizer) 

425 

426 rightpanel = wx.Panel(splitter) 

427 rtop = wx.Panel(rightpanel) 

428 

429 self.wid_scantitle = SimpleText( 

430 rtop, "<no scan selected>", font=Font(11), style=LEFT 

431 ) 

432 self.wid_scantime = SimpleText( 

433 rtop, "<no scan selected>", font=Font(11), style=LEFT 

434 ) 

435 self.title = SimpleText( 

436 rtop, 

437 "<no scan selected>", 

438 font=Font(11), 

439 colour=self.colors.title, 

440 style=LEFT, 

441 ) 

442 

443 yarr_labels = self.yarr_labels = ["1.0", "0.0", ""] 

444 xarr_labels = self.xarr_labels = ["_index"] 

445 

446 self.xarr = Choice( 

447 rtop, choices=xarr_labels, action=self.onXSelect, size=(150, -1) 

448 ) 

449 self.yarr1 = Choice( 

450 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1) 

451 ) 

452 self.yarr2 = Choice( 

453 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1) 

454 ) 

455 self.yerr_arr = Choice( 

456 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1) 

457 ) 

458 self.yerr_arr.Disable() 

459 

460 self.datatype = Choice( 

461 rtop, choices=XDATATYPES, action=self.onUpdate, size=(150, -1) 

462 ) 

463 self.datatype.SetStringSelection(self.workgroup.datatype) 

464 

465 self.en_units = Choice( 

466 rtop, choices=ENUNITS_TYPES, action=self.onEnUnitsSelect, size=(150, -1) 

467 ) 

468 

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

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

471 self.yerr_op = Choice( 

472 rtop, choices=YERR_OPS, action=self.onYerrChoice, size=(150, -1) 

473 ) 

474 

475 self.yerr_val = FloatCtrl(rtop, value=1, precision=4, size=(90, -1)) 

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

477 

478 xlab = SimpleText(rtop, " X array: ") 

479 ylab = SimpleText(rtop, " Y array: ") 

480 units_lab = SimpleText(rtop, " Units: ") 

481 yerr_lab = SimpleText(rtop, " Yerror: ") 

482 dtype_lab = SimpleText(rtop, " Data Type: ") 

483 monod_lab = SimpleText(rtop, " Mono D spacing (Ang): ") 

484 yerrval_lab = SimpleText(rtop, " Value:") 

485 

486 self.ysuf = SimpleText(rtop, "") 

487 self.message = SimpleText( 

488 rtop, "", font=Font(11), colour=self.colors.title, style=LEFT 

489 ) 

490 

491 self.ypop.SetStringSelection(self.array_sel["ypop"]) 

492 self.yop.SetStringSelection(self.array_sel["yop"]) 

493 self.monod_val.SetValue(self.array_sel["monod"]) 

494 self.monod_val.SetAction(self.onUpdate) 

495 

496 self.monod_val.Enable(self.array_sel["en_units"].startswith("deg")) 

497 self.en_units.SetStringSelection(self.array_sel["en_units"]) 

498 self.yerr_op.SetStringSelection(self.array_sel["yerror"]) 

499 self.yerr_val.SetValue(self.array_sel["yerr_val"]) 

500 if "(" in self.array_sel["ypop"]: 

501 self.ysuf.SetLabel(")") 

502 

503 sizer = wx.GridBagSizer(2, 2) 

504 ir = 0 

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

506 

507 ir += 1 

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

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

510 

511 ir += 1 

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

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

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

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

516 

517 ir += 1 

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

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

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

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

522 

523 ir += 1 

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

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

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

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

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

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

530 

531 ir += 1 

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

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

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

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

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

537 

538 ir += 1 

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

540 pack(rtop, sizer) 

541 

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

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

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

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

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

547 

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

549 try: 

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

551 self.plotpanel.conf.set_theme(plotopts["theme"]) 

552 self.plotpanel.conf.enable_grid(plotopts["show_grid"]) 

553 except Exception: 

554 pass 

555 

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

557 

558 shead = wx.Panel(rightpanel) 

559 self.scaninfo = wx.TextCtrl( 

560 shead, style=wx.TE_MULTILINE | wx.TE_READONLY, size=(400, 250) 

561 ) 

562 self.scaninfo.SetValue("<no scan selected>") 

563 self.scaninfo.SetFont(Font(10)) 

564 textsizer = wx.BoxSizer(wx.VERTICAL) 

565 textsizer.Add(self.scaninfo, 1, LEFT | wx.GROW, 1) 

566 pack(shead, textsizer) 

567 

568 fhead = wx.Panel(rightpanel) 

569 self.fileinfo = wx.TextCtrl( 

570 fhead, style=wx.TE_MULTILINE | wx.TE_READONLY, size=(400, 250) 

571 ) 

572 self.fileinfo.SetValue("") 

573 self.fileinfo.SetFont(Font(10)) 

574 textsizer = wx.BoxSizer(wx.VERTICAL) 

575 textsizer.Add(self.fileinfo, 1, LEFT | wx.GROW, 1) 

576 pack(fhead, textsizer) 

577 

578 self.nb.AddPage(fhead, " File Info ", True) 

579 self.nb.AddPage(shead, " Scan Info ", True) 

580 self.nb.AddPage(self.plotpanel, " Plot of Selected Arrays ", True) 

581 

582 sizer = wx.BoxSizer(wx.VERTICAL) 

583 sizer.Add(rtop, 0, LEFT | wx.GROW, 1) 

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

585 pack(rightpanel, sizer) 

586 

587 splitter.SplitVertically(leftpanel, rightpanel, 1) 

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

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

590 statusbar_fields = [filename, ""] 

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

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

593 

594 csize = self.GetSize() 

595 bsize = self.GetBestSize() 

596 if bsize[0] > csize[0]: 

597 csize[0] = bsize[0] 

598 if bsize[1] > csize[1]: 

599 csize[1] = bsize[1] 

600 self.SetSize(csize) 

601 self.Show() 

602 self.Raise() 

603 self.defaultScanSelect() 

604 

605 def set_energy_units(self): 

606 ix = self.xarr.GetSelection() 

607 xname = self.xarr.GetStringSelection() 

608 rdata = self.curscan.data 

609 try: 

610 ncol, npts = rdata.shape 

611 except Exception: 

612 self.statusbar.SetStatusText( 

613 f"Warning: Could not read data for scan '{self.curscan.name:s}'" 

614 ) 

615 ncol, npts = 0, 0 

616 

617 workgroup = self.workgroup 

618 if xname.startswith("_index") or ix >= ncol: 

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

620 else: 

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

622 eguess = guess_energy_units(workgroup.xplot) 

623 if eguess.startswith("eV"): 

624 self.en_units.SetStringSelection("eV") 

625 elif eguess.startswith("keV"): 

626 self.en_units.SetStringSelection("keV") 

627 

628 def defaultScanSelect(self): 

629 try: 

630 name = self.scans[-1] 

631 self.curscan = self.data_source.get_scan(name) 

632 except Exception: 

633 self.onUpdate() 

634 else: 

635 self.updateScanSelect() 

636 

637 def onScanSelect(self, event=None): 

638 try: 

639 scan_desc = event.GetString() 

640 name = [s.strip() for s in scan_desc.split(" | ")][0] 

641 self.curscan = self.data_source.get_scan(name) 

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

643 if scan_desc not in slist: 

644 slist.append(scan_desc) 

645 self.scanlist.SetCheckedStrings(slist) 

646 except Exception: 

647 pass 

648 else: 

649 self.updateScanSelect() 

650 

651 def updateScanSelect(self): 

652 if self.curscan is None: 

653 return 

654 

655 self.wid_scantitle.SetLabel(" %s" % self.curscan.description) 

656 self.wid_scantime.SetLabel(self.curscan.start_time) 

657 self.title.SetLabel(" %s, scan %s" % (self.path, self.curscan.name)) 

658 

659 arr_labels = [l.lower() for l in self.curscan.labels] 

660 yarr_labels = self.yarr_labels = arr_labels + ["1.0", "0.0", ""] 

661 xarr_labels = self.xarr_labels = arr_labels + ["_index"] 

662 

663 xsel = self.xarr.GetStringSelection() 

664 self.xarr.Clear() 

665 self.xarr.AppendItems(xarr_labels) 

666 if xsel in xarr_labels: 

667 self.xarr.SetStringSelection(xsel) 

668 else: 

669 self.xarr.SetSelection(0) 

670 

671 y1sel = self.yarr1.GetStringSelection() 

672 self.yarr1.Clear() 

673 self.yarr1.AppendItems(yarr_labels) 

674 if y1sel in yarr_labels: 

675 self.yarr1.SetStringSelection(y1sel) 

676 else: 

677 self.yarr1.SetSelection(1) 

678 

679 y2sel = self.yarr2.GetStringSelection() 

680 self.yarr2.Clear() 

681 self.yarr2.AppendItems(yarr_labels) 

682 if y2sel in yarr_labels: 

683 self.yarr2.SetStringSelection(y2sel) 

684 

685 xsel = self.xarr.GetStringSelection() 

686 self.workgroup.datatype = "xas" 

687 self.datatype.SetStringSelection(self.workgroup.datatype) 

688 

689 self.scaninfo.SetValue(self.curscan.info) 

690 self.set_energy_units() 

691 self.onUpdate() 

692 

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

694 shown = False 

695 if name in self.subframes: 

696 try: 

697 self.subframes[name].Raise() 

698 shown = True 

699 except Exception: 

700 pass 

701 if not shown: 

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

703 self.subframes[name].Show() 

704 self.subframes[name].Raise() 

705 

706 def onAddColumns(self, event=None): 

707 self.show_subframe( 

708 "addcol", AddColumnsFrame, group=self.workgroup, on_ok=self.add_columns 

709 ) 

710 

711 def add_columns(self, label, selection): 

712 new_labels = self.workgroup.array_labels 

713 self.set_array_labels(new_labels) 

714 self.yarr1.SetStringSelection(new_labels[-1]) 

715 self.extra_sums[label] = selection 

716 self.onUpdate() 

717 

718 def onEditNames(self, evt=None): 

719 self.show_subframe( 

720 "editcol", 

721 EditColumnFrame, 

722 group=self.workgroup, 

723 on_ok=self.set_array_labels, 

724 ) 

725 

726 def set_array_labels(self, arr_labels): 

727 self.workgroup.array_labels = arr_labels 

728 yarr_labels = self.yarr_labels = arr_labels + ["1.0", "0.0", ""] 

729 xarr_labels = self.xarr_labels = arr_labels + ["_index"] 

730 

731 def update(wid, choices): 

732 curstr = wid.GetStringSelection() 

733 curind = wid.GetSelection() 

734 wid.SetChoices(choices) 

735 if curstr in choices: 

736 wid.SetStringSelection(curstr) 

737 else: 

738 wid.SetSelection(curind) 

739 

740 update(self.xarr, xarr_labels) 

741 update(self.yarr1, yarr_labels) 

742 update(self.yarr2, yarr_labels) 

743 update(self.yerr_arr, yarr_labels) 

744 self.onUpdate() 

745 

746 def onSelAll(self, event=None): 

747 self.scanlist.SetCheckedStrings(self.scans) 

748 

749 def onSelNone(self, event=None): 

750 self.scanlist.SetCheckedStrings([]) 

751 

752 def onOK(self, event=None): 

753 """build arrays according to selection""" 

754 scanlist = [] 

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

756 words = [s.strip() for s in s.split("|")] 

757 scanlist.append(words[0]) 

758 if len(scanlist) == 0: 

759 cancel = Popup( 

760 self, 

761 """No scans selected. 

762 Cancel import from this project?""", 

763 "Cancel Import?", 

764 style=wx.YES_NO, 

765 ) 

766 if wx.ID_YES == cancel: 

767 self.Destroy() 

768 else: 

769 return 

770 

771 en_units = self.en_units.GetStringSelection() 

772 dspace = float(self.monod_val.GetValue()) 

773 xarr = self.xarr.GetStringSelection() 

774 yarr1 = self.yarr1.GetStringSelection() 

775 yarr2 = self.yarr2.GetStringSelection() 

776 ypop = self.ypop.GetStringSelection() 

777 yop = self.yop.GetStringSelection() 

778 yerr_op = self.yerr_op.GetStringSelection() 

779 yerr_arr = self.yerr_arr.GetStringSelection() 

780 yerr_idx = self.yerr_arr.GetSelection() 

781 yerr_val = self.yerr_val.GetValue() 

782 yerr_expr = "1" 

783 if yerr_op.startswith("const"): 

784 yerr_expr = "%f" % yerr_val 

785 elif yerr_op.lower().startswith("array"): 

786 yerr_expr = "%%s.data[%i, :]" % yerr_idx 

787 elif yerr_op.startswith("sqrt"): 

788 yerr_expr = "sqrt(%s.yplot)" 

789 self.expressions["yerr"] = yerr_expr 

790 

791 # generate script to pass back to calling program: 

792 # read_cmd = "_data_source.get_scan('{scan}')" 

793 buff = [ 

794 "{group} = {_data_source}.get_scan('{scan}')", 

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

796 "{group}.is_frozen = False", 

797 ] 

798 

799 for label, selection in self.extra_sums.items(): 

800 buff.append("{group}.array_labels.append('%s')" % label) 

801 buff.append("_tmparr = {group}.data[%s, :].sum(axis=0)" % repr(selection)) 

802 buff.append("_tmpn = len(_tmparr)") 

803 buff.append( 

804 "{group}.data = append({group}.data, _tmparr.reshape(1, _tmpn), axis=0)" 

805 ) 

806 buff.append("del _tmparr, _tmpn") 

807 

808 for attr in ("datatype", "plot_xlabel", "plot_ylabel"): 

809 val = getattr(self.workgroup, attr) 

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

811 

812 expr = self.expressions["xplot"].replace("%s", "{group:s}") 

813 if en_units.startswith("deg"): 

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

815 buff.append( 

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

817 ) 

818 elif en_units.startswith("keV"): 

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

820 else: 

821 buff.append(f"{ group} .xplot = {expr:s}") 

822 

823 for aname in ("yplot", "yerr"): 

824 expr = self.expressions[aname].replace("%s", "{group:s}") 

825 buff.append("{group}.%s = %s" % (aname, expr)) 

826 

827 if getattr(self.workgroup, "datatype", "xydata") == "xas": 

828 buff.append("{group}.energy = {group}.xplot") 

829 buff.append("{group}.mu = {group}.yplot") 

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

831 else: 

832 buff.append("{group}.scale = 1./(ptp({group}.yplot))+1.e-16)") 

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

834 

835 self.array_sel["xarr"] = xarr 

836 self.array_sel["yarr1"] = yarr1 

837 self.array_sel["yarr2"] = yarr2 

838 self.array_sel["yop"] = yop 

839 self.array_sel["ypop"] = ypop 

840 self.array_sel["yerror"] = yerr_op 

841 self.array_sel["yerr_val"] = yerr_val 

842 self.array_sel["yerr_arr"] = yerr_arr 

843 self.array_sel["monod"] = dspace 

844 self.array_sel["en_units"] = en_units 

845 

846 if self.read_ok_cb is not None: 

847 self.read_ok_cb(script, self.path, scanlist, array_sel=self.array_sel) 

848 

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

850 try: 

851 f.Destroy() 

852 except Exception: 

853 pass 

854 self.Destroy() 

855 

856 def onCancel(self, event=None): 

857 self.workgroup.import_ok = False 

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

859 try: 

860 f.Destroy() 

861 except Exception: 

862 pass 

863 self.Destroy() 

864 

865 def onYerrChoice(self, evt=None): 

866 yerr_choice = evt.GetString() 

867 self.yerr_arr.Disable() 

868 self.yerr_val.Disable() 

869 if "const" in yerr_choice.lower(): 

870 self.yerr_val.Enable() 

871 elif "array" in yerr_choice.lower(): 

872 self.yerr_arr.Enable() 

873 self.onUpdate() 

874 

875 def onXSelect(self, evt=None): 

876 ix = self.xarr.GetSelection() 

877 xname = self.xarr.GetStringSelection() 

878 

879 workgroup = self.workgroup 

880 rdata = self.curscan.data 

881 ncol, npts = rdata.shape 

882 if xname.startswith("_index") or ix >= ncol: 

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

884 else: 

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

886 

887 self.monod_val.Disable() 

888 if self.datatype.GetStringSelection().strip().lower() == "xydata": 

889 self.en_units.SetSelection(4) 

890 else: 

891 eguess = guess_energy_units(workgroup.xplot) 

892 if eguess.startswith("keV"): 

893 self.en_units.SetSelection(1) 

894 elif eguess.startswith("deg"): 

895 self.en_units.SetSelection(2) 

896 self.monod_val.Enable() 

897 else: 

898 self.en_units.SetSelection(0) 

899 

900 self.onUpdate() 

901 

902 def onEnUnitsSelect(self, evt=None): 

903 self.monod_val.Enable(self.en_units.GetStringSelection().startswith("deg")) 

904 self.onUpdate() 

905 

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

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

908 # dtcorr = self.dtcorr.IsChecked() 

909 workgroup = self.workgroup 

910 rdata = self.curscan.data 

911 

912 dtcorr = False 

913 

914 ix = self.xarr.GetSelection() 

915 xname = self.xarr.GetStringSelection() 

916 yname1 = self.yarr1.GetStringSelection().strip() 

917 yname2 = self.yarr2.GetStringSelection().strip() 

918 iy1 = self.yarr1.GetSelection() 

919 iy2 = self.yarr2.GetSelection() 

920 yop = self.yop.GetStringSelection().strip() 

921 

922 exprs = dict(xplot=None, yplot=None, yerr=None) 

923 

924 ncol, npts = rdata.shape 

925 workgroup.index = 1.0 * np.arange(npts) 

926 if xname.startswith("_index") or ix >= ncol: 

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

928 xname = "_index" 

929 exprs["xplot"] = "arange(%i)" % npts 

930 else: 

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

932 exprs["xplot"] = "%%s.data[%i, : ]" % ix 

933 

934 xlabel = xname 

935 en_units = self.en_units.GetStringSelection() 

936 if en_units.startswith("deg"): 

937 dspace = float(self.monod_val.GetValue()) 

938 workgroup.xplot = PLANCK_HC / (2 * dspace * np.sin(DEG2RAD * workgroup.xplot)) 

939 xlabel = xname + " (eV)" 

940 elif en_units.startswith("keV"): 

941 workgroup.xplot *= 1000.0 

942 xlabel = xname + " (eV)" 

943 

944 workgroup.datatype = self.datatype.GetStringSelection().strip().lower() 

945 

946 def pre_op(opwid, arr): 

947 opstr = opwid.GetStringSelection().strip() 

948 suf = "" 

949 if opstr in ("-log(", "log("): 

950 suf = ")" 

951 if opstr == "log(": 

952 arr = np.log(arr) 

953 elif opstr == "-log(": 

954 arr = -np.log(arr) 

955 arr[np.where(np.isnan(arr))] = 0 

956 return suf, opstr, arr 

957 

958 ylabel = yname1 

959 if len(yname2) == 0: 

960 yname2 = "1.0" 

961 else: 

962 ylabel = "%s%s%s" % (ylabel, yop, yname2) 

963 

964 if yname1 == "0.0": 

965 yarr1 = np.zeros(npts) * 1.0 

966 yexpr1 = "zeros(%i)" % npts 

967 elif len(yname1) == 0 or yname1 == "1.0" or iy1 >= ncol: 

968 yarr1 = np.ones(npts) * 1.0 

969 yexpr1 = "ones(%i)" % npts 

970 else: 

971 yarr1 = rdata[iy1, :] 

972 yexpr1 = "%%s.data[%i, : ]" % iy1 

973 

974 if yname2 == "0.0": 

975 yarr2 = np.zeros(npts) * 1.0 

976 yexpr2 = "0.0" 

977 elif len(yname2) == 0 or yname2 == "1.0" or iy2 >= ncol: 

978 yarr2 = np.ones(npts) * 1.0 

979 yexpr2 = "1.0" 

980 else: 

981 yarr2 = rdata[iy2, :] 

982 yexpr2 = "%%s.data[%i, : ]" % iy2 

983 

984 workgroup.yplot = yarr1 

985 

986 exprs["yplot"] = yexpr1 

987 if yop in ("+", "-", "*", "/"): 

988 exprs["yplot"] = "%s %s %s" % (yexpr1, yop, yexpr2) 

989 if yop == "+": 

990 workgroup.yplot = yarr1.__add__(yarr2) 

991 elif yop == "-": 

992 workgroup.yplot = yarr1.__sub__(yarr2) 

993 elif yop == "*": 

994 workgroup.yplot = yarr1.__mul__(yarr2) 

995 elif yop == "/": 

996 workgroup.yplot = yarr1.__truediv__(yarr2) 

997 

998 ysuf, ypop, workgroup.yplot = pre_op(self.ypop, workgroup.yplot) 

999 exprs["yplot"] = "%s%s%s" % (ypop, exprs["yplot"], ysuf) 

1000 

1001 yerr_op = self.yerr_op.GetStringSelection().lower() 

1002 exprs["yerr"] = "1" 

1003 if yerr_op.startswith("const"): 

1004 yerr = self.yerr_val.GetValue() 

1005 exprs["yerr"] = "%f" % yerr 

1006 elif yerr_op.startswith("array"): 

1007 iyerr = self.yerr_arr.GetSelection() 

1008 yerr = rdata[iyerr, :] 

1009 exprs["yerr"] = "%%s.data[%i, :]" % iyerr 

1010 elif yerr_op.startswith("sqrt"): 

1011 yerr = np.sqrt(workgroup.yplot) 

1012 exprs["yerr"] = "sqrt(%s.yplot)" 

1013 

1014 self.expressions = exprs 

1015 self.array_sel = { 

1016 "xarr": xname, 

1017 "ypop": ypop, 

1018 "yop": yop, 

1019 "yarr1": yname1, 

1020 "yarr2": yname2, 

1021 } 

1022 try: 

1023 npts = min(len(workgroup.xplot), len(workgroup.yplot)) 

1024 except AttributeError: 

1025 return 

1026 except ValueError: 

1027 return 

1028 

1029 en = workgroup.xplot 

1030 if (workgroup.datatype == "xas") and ( 

1031 ( 

1032 len(en) > 1000 

1033 or any(np.diff(en) < 0) 

1034 or ((max(en) - min(en)) > 350 and (np.diff(en[:100]).mean() < 1.0)) 

1035 ) 

1036 ): 

1037 self.statusbar.SetStatusText("Warning: XAS data may need to be rebinned!") 

1038 

1039 workgroup.filename = self.path 

1040 workgroup.npts = npts 

1041 workgroup.plot_xlabel = xlabel 

1042 workgroup.plot_ylabel = ylabel 

1043 workgroup.xplot = np.array(workgroup.xplot[:npts]) 

1044 workgroup.yplot = np.array(workgroup.yplot[:npts]) 

1045 workgroup.y = workgroup.yplot 

1046 workgroup.yerr = yerr 

1047 if isinstance(yerr, np.ndarray): 

1048 workgroup.yerr = np.array(yerr[:npts]) 

1049 

1050 if workgroup.datatype == "xas": 

1051 workgroup.energy = workgroup.xplot 

1052 workgroup.mu = workgroup.yplot 

1053 elif workgroup.datatype == 'xydata': 

1054 workgroup.x = workgroup.xplot[:] 

1055 workgroup.y = workgroup.yplot[:] 

1056 workgroup.xshift = 0.0 

1057 workgroup.scale = np.ptp(workgroup.y+1.e-15) 

1058 

1059 fname = Path(workgroup.filename).name 

1060 popts = dict( 

1061 marker="o", 

1062 markersize=4, 

1063 linewidth=1.5, 

1064 title=fname, 

1065 ylabel=ylabel, 

1066 xlabel=xlabel, 

1067 label="%s: %s" % (fname, workgroup.plot_ylabel), 

1068 ) 

1069 try: 

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

1071 except Exception: 

1072 pass 

1073 

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

1075 self.statusbar.SetStatusText(msg, panel)