Coverage for /Users/Newville/Codes/xraylarch/larch/wxlib/columnframe.py: 7%

1093 statements  

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

1#!/usr/bin/env python 

2""" 

3 

4""" 

5import os 

6import re 

7from copy import deepcopy 

8 

9import numpy as np 

10np.seterr(all='ignore') 

11 

12from functools import partial 

13 

14import wx 

15import wx.lib.scrolledpanel as scrolled 

16import wx.lib.agw.flatnotebook as fnb 

17from wxmplot import PlotPanel 

18 

19from wxutils import (SimpleText, FloatCtrl, FloatSpin, GUIColors, Button, Choice, 

20 TextCtrl, pack, Popup, Check, MenuItem, CEN, RIGHT, LEFT, 

21 FRAMESTYLE, HLine, Font) 

22 

23import larch 

24from larch import Group 

25from larch.xafs.xafsutils import guess_energy_units 

26from larch.utils.strutils import fix_varname, fix_filename, file2groupname 

27from larch.io import look_for_nans, guess_filereader, is_specfile, sum_fluor_channels 

28from larch.utils.physical_constants import PLANCK_HC, DEG2RAD 

29from larch.utils import gformat 

30from larch.math import safe_log 

31from . import FONTSIZE 

32 

33CEN |= wx.ALL 

34FNB_STYLE = fnb.FNB_NO_X_BUTTON|fnb.FNB_SMART_TABS 

35FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NODRAG 

36 

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

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

39 

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

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

42 

43DATATYPES = ('raw', 'xas') 

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

45 

46 

47MULTICHANNEL_TITLE = """ Sum MultiChannel Fluorescence Data, with Dead-Time Corrections: 

48 To allow for many Dead-Time-Correction methods, each Channel is built as: 

49 ROI_Corrected = ROI * ICR /(OCR * LTIME) 

50 

51 Set the Number of Channels, the Step (usually 1) between columns for 

52 ROI 1, 2, ..., NChans, and any Bad Channels: a list of Channel numbers (start at 1). 

53 

54 Select columns for ROI (counts) and correction factors ICR, OCR, and LTIME for Channel 1. 

55 

56""" 

57 

58ROI_STEP_TOOLTIP = """number of columns between ROI columns -- typically 1 if the columns are like 

59 ROI_Ch1 ROI_Ch2 ROI_Ch3 ... ICR_Ch1 ICR_Ch2 ICR_Ch3 ... OCR_Ch1 OCR_Ch2 OCR_Ch3 ... 

60 

61but set to 3 if the columns are arranged as 

62 ROI_Ch1 ICR_Ch1 OCR_Ch1 ROI_Ch2 ICR_Ch2 OCR_Ch2 ROI_Ch3 ICR_Ch3 OCR_Ch3 ... 

63""" 

64MAXCHANS=2000 

65 

66class DeadtimeCorrectionFrame(wx.Frame): 

67 """Manage MultiChannel Fluorescence Data""" 

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

69 self.parent = parent 

70 self.group = group 

71 self.config = {'bad_chans': [], 'plot_chan': 1, 'nchans': 4, 'step': 1, 

72 'roi': '1.0', 'icr': '1.0', 'ocr': '1.0', 

73 'ltime': '1.0', 'i0': '1.0'} 

74 # 'out_choice': 'summed spectrum', 

75 if config is not None: 

76 self.config.update(config) 

77 self.arrays = {} 

78 self.on_ok = on_ok 

79 wx.Frame.__init__(self, None, -1, 'MultiChannel Fluorescence Data', 

80 style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL) 

81 

82 self.SetFont(Font(FONTSIZE)) 

83 sizer = wx.GridBagSizer(2, 2) 

84 panel = scrolled.ScrolledPanel(self) 

85 

86 self.SetMinSize((650, 450)) 

87 self.yarr_labels = [s for s in self.parent.yarr_labels] 

88 wids = self.wids = {} 

89 

90 multi_title = wx.StaticText(panel, label=MULTICHANNEL_TITLE, size=(650, 150)) 

91 multi_title.SetFont(Font(FONTSIZE-1)) 

92 for s in ('roi', 'icr', 'ocr', 'ltime'): 

93 wids[s] = Choice(panel, choices=self.yarr_labels, action=self.read_form, size=(150, -1)) 

94 sel = self.config.get(s, '1.0') 

95 if sel == '1.0': 

96 wids[s].SetStringSelection(sel) 

97 else: 

98 wids[s].SetSelection(sel[0]) 

99 wids[f'{s}_txt'] = SimpleText(panel, label='<list of column labels>', size=(275, -1)) 

100 

101 wids['i0'] = Choice(panel, choices=self.yarr_labels, action=self.read_form, size=(150, -1)) 

102 wids['i0'].SetToolTip("All Channels will be divided by the I0 array") 

103 

104 wids['i0'].SetStringSelection(self.parent.yarr2.GetStringSelection()) 

105 

106 wids['nchans'] = FloatCtrl(panel, value=self.config.get('nchans', 4), 

107 precision=0, maxval=MAXCHANS, minval=1, size=(50, -1), 

108 action=self.on_nchans) 

109 wids['bad_chans'] = TextCtrl(panel, value='', size=(175, -1), action=self.read_form) 

110 bad_chans = self.config.get('bad_chans', []) 

111 if len(bad_chans) > 0: 

112 wids['bad_chans'].SetValue(', '.join(['%d' % c for c in bad_chans])) 

113 wids['bad_chans'].SetToolTip("List Channels to skip, separated by commas or spaces") 

114 wids['step'] = FloatCtrl(panel, value=self.config.get('step', 1), precision=0, 

115 maxval=MAXCHANS, minval=1, size=(50, -1), action=self.read_form) 

116 wids['step'].SetToolTip(ROI_STEP_TOOLTIP) 

117 

118 wids['plot_chan'] = FloatSpin(panel, value=self.config.get('plot_chan', 1), 

119 digits=0, increment=1, max_val=MAXCHANS, min_val=1, size=(50, -1), 

120 action=self.onPlotThis) 

121 

122 wids['plot_this'] = Button(panel, 'Plot ROI + Correction For Channel', action=self.onPlotThis) 

123 wids['plot_all'] = Button(panel, 'Plot All Channels', action=self.onPlotEach) 

124 wids['plot_sum'] = Button(panel, 'Plot Sum of Channels', action=self.onPlotSum) 

125 wids['save_btn'] = Button(panel, 'Use this Sum of Channels', action=self.onOK_DTC) 

126 

127 def tlabel(t): 

128 return SimpleText(panel, label=t) 

129 

130 sizer.Add(multi_title, (0, 0), (2, 5), LEFT, 3) 

131 ir = 2 

132 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 5), LEFT, 3) 

133 

134 ir += 1 

135 sizer.Add(tlabel(' Number of Channels:'), (ir, 0), (1, 1), LEFT, 3) 

136 sizer.Add(wids['nchans'], (ir, 1), (1, 1), LEFT, 3) 

137 sizer.Add(tlabel(' Step between Channels:'), (ir, 2), (1, 1), LEFT, 3) 

138 sizer.Add(wids['step'], (ir, 3), (1, 1), LEFT, 3) 

139 

140 ir += 1 

141 sizer.Add(tlabel(' Bad Channels :'), (ir, 0), (1, 1), LEFT, 3) 

142 sizer.Add(wids['bad_chans'], (ir, 1), (1, 2), LEFT, 3) 

143 

144 ir += 1 

145 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 5), LEFT, 3) 

146 

147 ir += 1 

148 sizer.Add(tlabel(' Signal '), (ir, 0), (1, 1), LEFT, 3) 

149 sizer.Add(tlabel(' Array for Channel #1 '), (ir, 1), (1, 1), LEFT, 3) 

150 sizer.Add(tlabel(' Array Labels used for all Channels '), (ir, 2), (1, 3), LEFT, 3) 

151 

152 for s in ('roi', 'icr', 'ocr', 'ltime'): 

153 ir += 1 

154 sizer.Add(tlabel(f' {s.upper()} #1 : '), (ir, 0), (1, 1), LEFT, 3) 

155 sizer.Add(wids[s], (ir, 1), (1, 1), LEFT, 3) 

156 sizer.Add(wids[f'{s}_txt'], (ir, 2), (1, 3), LEFT, 3) 

157 

158 ir += 1 

159 sizer.Add(tlabel(' I0 : '), (ir, 0), (1, 1), LEFT, 3) 

160 sizer.Add(wids['i0'], (ir, 1), (1, 1), LEFT, 3) 

161 

162 ir += 1 

163 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 5), LEFT, 3) 

164 

165 ir += 1 

166 sizer.Add(wids['plot_this'], (ir, 0), (1, 2), LEFT, 3) 

167 sizer.Add(tlabel(' Channel:'), (ir, 2), (1, 1), LEFT, 3) 

168 sizer.Add(wids['plot_chan'], (ir, 3), (1, 1), LEFT, 3) 

169 

170 ir += 1 

171 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 5), LEFT, 3) 

172 ir += 1 

173 sizer.Add(wids['plot_all'], (ir, 0), (1, 1), LEFT, 3) 

174 sizer.Add(wids['plot_sum'], (ir, 1), (1, 1), LEFT, 3) 

175 sizer.Add(wids['save_btn'], (ir, 2), (1, 2), LEFT, 3) 

176 

177 pack(panel, sizer) 

178 panel.SetupScrolling() 

179 

180 mainsizer = wx.BoxSizer(wx.VERTICAL) 

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

182 

183 pack(self, mainsizer) 

184 self.Show() 

185 self.Raise() 

186 

187 def get_en_i0(self): 

188 en = self.group.xdat 

189 i0 = 1.0 

190 if self.config['i0'] != '1.0': 

191 i0 = self.group.data[self.config['i0'], :] 

192 return en, i0 

193 

194 def read_arrays(self, pchan): 

195 def get_array(name, pchan, default=1.0): 

196 out = default 

197 if self.config[name] != '1.0': 

198 ix = self.config[name][pchan-1] 

199 if ix > 0: 

200 out = self.group.data[ix, :] 

201 return out 

202 roi = get_array('roi', pchan, default=None) 

203 icr = get_array('icr', pchan) 

204 ocr = get_array('ocr', pchan) 

205 ltime = get_array('ltime', pchan) 

206 return roi, icr*ocr/ltime 

207 

208 def onOK_DTC(self, event=None): 

209 self.read_form() 

210 if callable(self.on_ok): 

211 self.on_ok(self.config) 

212 self.Destroy() 

213 

214 def onPlotSum(self, event=None): 

215 self.read_form() 

216 en, i0 = self.get_en_i0() 

217 label, sum = sum_fluor_channels(self.group, self.config['roi'], 

218 icr=self.config['icr'], 

219 ocr=self.config['ocr'], 

220 ltime=self.config['ltime'], 

221 add_data=False) 

222 if sum is not None: 

223 popts = dict(marker=None, markersize=0, linewidth=2.5, 

224 show_legend=True, ylabel=label, label=label, 

225 xlabel='Energy (eV)') 

226 self.parent.plotpanel.plot(en, sum/i0, new=True, **popts) 

227 

228 def onPlotEach(self, event=None): 

229 self.read_form() 

230 new = True 

231 en, i0 = self.get_en_i0() 

232 popts = dict(marker=None, markersize=0, linewidth=2.5, 

233 show_legend=True, xlabel='Energy (eV)', 

234 ylabel=f'Corrected Channels') 

235 

236 nused = 0 

237 for pchan in range(1, self.config['nchans']+1): 

238 roi, dtc = self.read_arrays(pchan) 

239 if roi is not None: 

240 popts['label'] = f'Chan{pchan} Corrected' 

241 if new: 

242 self.parent.plotpanel.plot(en, roi*dtc/i0, new=True, **popts) 

243 new = False 

244 else: 

245 self.parent.plotpanel.oplot(en, roi*dtc/i0, **popts) 

246 

247 def onPlotThis(self, event=None): 

248 self.read_form() 

249 en, i0 = self.get_en_i0() 

250 pchan = self.config['plot_chan'] 

251 roi, dtc = self.read_arrays(pchan) 

252 if roi is None: 

253 return 

254 ylabel = self.wids['roi'].GetStringSelection() 

255 popts = dict(marker=None, markersize=0, linewidth=2.5, show_legend=True, 

256 ylabel=f'Chan{pchan}', xlabel='Energy (eV)', 

257 label=f'Chan{pchan} Raw') 

258 

259 self.parent.plotpanel.plot(en, roi/i0, new=True, **popts) 

260 popts['label'] = f'Chan{pchan} Corrected' 

261 self.parent.plotpanel.oplot(en, roi*dtc/i0, **popts) 

262 

263 def on_nchans(self, event=None, value=None, **kws): 

264 try: 

265 nchans = self.wids['nchans'].GetValue() 

266 pchan = self.wids['plot_chan'].GetValue() 

267 self.wids['plot_chan'].SetMax(nchans) 

268 self.wids['plot_chan'].SetValue(pchan) 

269 except: 

270 pass 

271 

272 def read_form(self, event=None, value=None, **kws): 

273 try: 

274 wids = self.wids 

275 nchans = int(wids['nchans'].GetValue()) 

276 step = int(wids['step'].GetValue()) 

277 badchans = wids['bad_chans'].GetValue().replace(',', ' ').strip() 

278 except: 

279 return 

280 

281 bad_channels = [] 

282 if len(badchans) > 0: 

283 try: 

284 bad_channels = [int(s) for s in badchans.split()] 

285 wids['bad_chans'].SetBackgroundColour('#FFFFFF') 

286 except: 

287 bad_channels = [] 

288 wids['bad_chans'].SetBackgroundColour('#F0B03080') 

289 

290 pchan = int(wids['plot_chan'].GetValue()) 

291 

292 self.config['bad_chans'] = bad_channels 

293 self.config['plot_chan'] = pchan 

294 self.config['nchans'] = nchans 

295 self.config['step'] = step 

296 self.config['i0'] = wids['i0'].GetSelection() 

297 if wids['i0'].GetStringSelection() in ('1.0', ''): 

298 self.config['i0'] = '1.0' 

299 

300 for s in ('roi', 'icr', 'ocr', 'ltime'): 

301 lab = wids[s].GetStringSelection() 

302 ilab = wids[s].GetSelection() 

303 if lab in ('1.0', ''): 

304 wids[f"{s}_txt"].SetLabel(lab) 

305 wids[f"{s}_txt"].SetToolTip(lab) 

306 self.config[s] = '1.0' 

307 else: 

308 chans = [ilab + i*step for i in range(nchans)] 

309 labs = [] 

310 for i in range(nchans): 

311 if (i+1) in bad_channels: 

312 chans[i] = -1 

313 else: 

314 nchan = chans[i] 

315 if nchan < len(self.group.array_labels): 

316 labs.append(self.group.array_labels[nchan]) 

317 wids[f"{s}_txt"].SetLabel(', '.join(labs)) 

318 self.config[s] = chans 

319 

320 

321class MultiColumnFrame(wx.Frame) : 

322 """Select Multiple Columns for import, optional i0 channel""" 

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

324 self.parent = parent 

325 self.group = group 

326 

327 self.on_ok = on_ok 

328 wx.Frame.__init__(self, None, -1, 'Import Multiple Columns from a file', 

329 style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL) 

330 

331 self.config = {'channels': [], 'i0': '1.0'} 

332 if config is not None: 

333 self.config.update(config) 

334 

335 self.SetFont(Font(FONTSIZE)) 

336 sizer = wx.GridBagSizer(2, 2) 

337 panel = scrolled.ScrolledPanel(self) 

338 

339 self.SetMinSize((475, 350)) 

340 self.yarr_labels = [s for s in self.parent.yarr_labels] 

341 wids = self.wids = {} 

342 

343 wids['i0'] = Choice(panel, choices=self.yarr_labels, size=(200, -1)) 

344 wids['i0'].SetToolTip("All Channels will be divided by the I0 array") 

345 

346 wids['i0'].SetStringSelection(self.parent.yarr2.GetStringSelection()) 

347 

348 bpanel = wx.Panel(panel) 

349 bsizer = wx.BoxSizer(wx.HORIZONTAL) 

350 

351 bsizer.Add(Button(bpanel, ' Select All ', action=self.onSelAll)) 

352 bsizer.Add(Button(bpanel, ' Select None ', action=self.onSelNone)) 

353 bsizer.Add(Button(bpanel, ' Import Selected Columns ', action=self.onOK_Multi)) 

354 pack(bpanel, bsizer) 

355 

356 ir = 0 

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

358 ir += 1 

359 sizer.Add(HLine(panel, size=(450, 2)), (ir, 0), (1, 5), LEFT, 3) 

360 

361 ir += 1 

362 sizer.Add(SimpleText(panel, label=' I0 Array: '), (ir, 0), (1, 1), LEFT, 3) 

363 sizer.Add(wids['i0'], (ir, 1), (1, 3), LEFT, 3) 

364 

365 ir += 1 

366 sizer.Add(SimpleText(panel, label=' Array Name'), (ir, 0), (1, 1), LEFT, 3) 

367 sizer.Add(SimpleText(panel, label=' Import? '), (ir, 1), (1, 1), LEFT, 3) 

368 sizer.Add(SimpleText(panel, label=' Plot'), (ir, 2), (1, 1), LEFT, 3) 

369 

370 array_labels = getattr(group, 'array_labels', self.yarr_labels) 

371 nlabels = len(array_labels) 

372 narrays, npts = group.data.shape 

373 for i in range(narrays): 

374 if i < nlabels: 

375 name = array_labels[i] 

376 else: 

377 name = f'unnamed_column{i+1}' 

378 self.wids[f'use_{i}'] = chuse = Check(panel, label='', default=(i in self.config['channels'])) 

379 slabel = SimpleText(panel, label=f' {name} ') 

380 bplot = Button(panel, 'Plot', action=partial(self.onPlot, index=i)) 

381 

382 ir += 1 

383 sizer.Add(slabel, (ir, 0), (1, 1), LEFT, 3) 

384 sizer.Add(chuse, (ir, 1), (1, 1), LEFT, 3) 

385 sizer.Add(bplot, (ir, 2), (1, 1), LEFT, 3) 

386 

387 pack(panel, sizer) 

388 panel.SetupScrolling() 

389 

390 mainsizer = wx.BoxSizer(wx.VERTICAL) 

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

392 

393 pack(self, mainsizer) 

394 self.Show() 

395 self.Raise() 

396 

397 

398 def onSelAll(self, event=None, *kws): 

399 for wname, wid in self.wids.items(): 

400 if wname.startswith('use_'): 

401 wid.SetValue(1) 

402 

403 def onSelNone(self, event=None, *kws): 

404 for wname, wid in self.wids.items(): 

405 if wname.startswith('use_'): 

406 wid.SetValue(0) 

407 

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

409 if index is not None: 

410 x = self.group.xdat 

411 y = self.group.data[index, :] 

412 try: 

413 label = self.group.array_labels[index] 

414 except: 

415 label = f'column {index+1}' 

416 

417 popts = dict(marker=None, markersize=0, linewidth=2.5, 

418 ylabel=label, xlabel=self.group.plot_xlabel, label=label) 

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

420 

421 def onOK_Multi(self, evt=None): 

422 group = self.group 

423 self.config['i0'] = self.wids['i0'].GetSelection() 

424 channels = [] 

425 for wname, wid in self.wids.items(): 

426 if wname.startswith('use_') and wid.IsChecked(): 

427 chan = int(wname.replace('use_', '')) 

428 channels.append(chan) 

429 

430 self.config['channels'] = channels 

431 if callable(self.on_ok): 

432 self.on_ok(self.config) 

433 self.Destroy() 

434 

435 

436class EditColumnFrame(wx.Frame) : 

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

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

439 self.parent = parent 

440 self.group = group 

441 self.on_ok = on_ok 

442 wx.Frame.__init__(self, None, -1, 'Edit Array Names', 

443 style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL) 

444 

445 self.SetFont(Font(FONTSIZE)) 

446 sizer = wx.GridBagSizer(2, 2) 

447 panel = scrolled.ScrolledPanel(self) 

448 

449 self.SetMinSize((700, 450)) 

450 

451 self.wids = {} 

452 ir = 0 

453 sizer.Add(Button(panel, 'Apply Changes', size=(200, -1), 

454 action=self.onOK_Edit), 

455 (0, 1), (1, 2), LEFT, 3) 

456 sizer.Add(Button(panel, 'Use Column Number', size=(200, -1), 

457 action=self.onColNumber), 

458 (0, 3), (1, 2), LEFT, 3) 

459 sizer.Add(HLine(panel, size=(550, 2)), 

460 (1, 1), (1, 5), LEFT, 3) 

461 

462 cind = SimpleText(panel, label='Column') 

463 cold = SimpleText(panel, label=' Current Name') 

464 cnew = SimpleText(panel, label=' Enter New Name') 

465 cret = SimpleText(panel, label=' Result ') 

466 cinfo = SimpleText(panel, label=' Data Range') 

467 cplot = SimpleText(panel, label=' Plot') 

468 

469 ir = 2 

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

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

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

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

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

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

476 

477 nlabels = len(group.array_labels) 

478 narrays, npts = group.data.shape 

479 for i in range(narrays): 

480 if i < nlabels: 

481 name = group.array_labels[i] 

482 else: 

483 name = f'unnamed_column{i+1}' 

484 ir += 1 

485 cind = SimpleText(panel, label=f' {i+1} ') 

486 cold = SimpleText(panel, label=f' {name} ') 

487 cret = SimpleText(panel, label=fix_varname(name)) 

488 cnew = wx.TextCtrl(panel, value=name, size=(150, -1), 

489 style=wx.TE_PROCESS_ENTER) 

490 

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

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

493 

494 arr = group.data[i,:] 

495 info_str = f" [{gformat(arr.min(),length=9)}:{gformat(arr.max(), length=9)}] " 

496 cinfo = SimpleText(panel, label=info_str) 

497 cplot = Button(panel, 'Plot', action=partial(self.onPlot, index=i)) 

498 

499 

500 self.wids[f"{i}"] = cnew 

501 self.wids[f"ret_{i}"] = cret 

502 

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

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

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

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

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

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

509 

510 pack(panel, sizer) 

511 panel.SetupScrolling() 

512 

513 mainsizer = wx.BoxSizer(wx.VERTICAL) 

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

515 

516 pack(self, mainsizer) 

517 self.Show() 

518 self.Raise() 

519 

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

521 if index is not None: 

522 x = self.group.index 

523 y = self.group.data[index, :] 

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

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

526 ylabel=label, xlabel='data point', label=label) 

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

528 

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

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

531 val = name 

532 if name.startswith('ret_'): 

533 val = name[4:] 

534 setter = wid.SetLabel 

535 else: 

536 setter = wid.SetValue 

537 setter("col%d" % (int(val) +1)) 

538 

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

540 newval = fix_varname(self.wids[f"{index}"].GetValue()) 

541 self.wids[f"ret_{index}"].SetLabel(newval) 

542 

543 def update_char(self, evt=None, index=-1): 

544 if evt.GetKeyCode() == wx.WXK_RETURN: 

545 self.update(evt=evt, index=index) 

546 # evt.Skip() 

547 

548 def onOK_Edit(self, evt=None): 

549 group = self.group 

550 array_labels = [] 

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

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

553 array_labels.append(newname) 

554 

555 if callable(self.on_ok): 

556 self.on_ok(array_labels) 

557 self.Destroy() 

558 

559class ColumnDataFileFrame(wx.Frame) : 

560 """Column Data File, select columns""" 

561 def __init__(self, parent, filename=None, groupname=None, config=None, 

562 read_ok_cb=None, edit_groupname=True, _larch=None): 

563 self.parent = parent 

564 self._larch = _larch 

565 self.path = filename 

566 

567 group = self.read_column_file(self.path) 

568 

569 self.subframes = {} 

570 self.workgroup = Group(raw=group) 

571 for attr in ('path', 'filename', 'groupname', 'datatype', 

572 'array_labels', 'data'): 

573 setattr(self.workgroup, attr, getattr(group, attr, None)) 

574 

575 self.array_labels = [l.lower() for l in group.array_labels] 

576 

577 if self.workgroup.datatype is None: 

578 self.workgroup.datatype = 'raw' 

579 en_units = 'not energy' 

580 for arrlab in self.array_labels[:3]: 

581 arrlab = arrlab.lower() 

582 if arrlab.startswith('en') or 'ener' in arrlab: 

583 en_units = 'eV' 

584 self.workgroup.datatype = 'xas' 

585 

586 self.read_ok_cb = read_ok_cb 

587 self.config = dict(xarr=None, yarr1=None, yarr2=None, yop='/', 

588 ypop='', monod=3.1355316, en_units=en_units, 

589 yerr_op='constant', yerr_val=1, yerr_arr=None, 

590 yrpop='', yrop='/', yref1='', yref2='', 

591 is_trans=False, 

592 has_yref=False, dtc_config={}, multicol_config={}) 

593 if config is not None: 

594 self.config.update(config) 

595 dtype = config.get('datatype', None) 

596 if dtype in ('xas', 'raw'): 

597 self.workgroup.datatype = dtype 

598 

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

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

601 

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

603 if 'itrans' in self.array_labels: 

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

605 elif 'i1' in self.array_labels: 

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

607 

608 if self.config['yref1'] is None: 

609 if 'iref' in self.array_labels: 

610 self.config['yref1'] = 'iref' 

611 elif 'irefer' in self.array_labels: 

612 self.config['yref1'] = 'irefer' 

613 elif 'i2' in self.array_labels: 

614 self.config['yref1'] = 'i2' 

615 

616 if self.config['yref2'] is None and 'i1' in self.array_labels: 

617 self.config['yref2'] = 'i1' 

618 

619 message = "Data Columns for %s" % group.filename 

620 wx.Frame.__init__(self, None, -1, 

621 'Build Arrays from Data Columns for %s' % group.filename, 

622 style=FRAMESTYLE) 

623 

624 x0, y0 = parent.GetPosition() 

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

626 

627 self.SetFont(Font(FONTSIZE)) 

628 panel = wx.Panel(self) 

629 self.SetMinSize((725, 700)) 

630 self.colors = GUIColors() 

631 

632 def subtitle(s, fontsize=12, colour=wx.Colour(10, 10, 180)): 

633 return SimpleText(panel, s, font=Font(fontsize), 

634 colour=colour, style=LEFT) 

635 

636 # title row 

637 title = subtitle(message, colour=self.colors.title) 

638 

639 yarr_labels = self.yarr_labels = self.array_labels + ['1.0', ''] 

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

641 

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

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

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

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

646 self.yerr_arr.Disable() 

647 

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

649 self.datatype.SetStringSelection(self.workgroup.datatype) 

650 

651 self.en_units = Choice(panel, choices=ENUNITS_TYPES, 

652 action=self.onEnUnitsSelect, size=(150, -1)) 

653 

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

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

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

657 self.yerr_op.SetSelection(0) 

658 

659 self.is_trans = Check(panel, label='is transmission data?', 

660 default=self.config['is_trans'], 

661 action=self.onTransCheck) 

662 

663 self.yerr_val = FloatCtrl(panel, value=1, precision=4, size=(75, -1)) 

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

665 

666 xlab = SimpleText(panel, ' X array = ') 

667 ylab = SimpleText(panel, ' Y array = ') 

668 units_lab = SimpleText(panel, ' Units of X array: ') 

669 yerr_lab = SimpleText(panel, ' Y uncertainty = ') 

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

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

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

673 self.info_message = subtitle(' ', colour=wx.Colour(100, 10, 10)) 

674 

675 # yref 

676 self.has_yref = Check(panel, label='data file includes energy reference data', 

677 default=self.config['has_yref'], 

678 action=self.onYrefCheck) 

679 refylab = SimpleText(panel, ' Refer array = ') 

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

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

682 self.yrpop = Choice(panel, choices=YPRE_OPS, action=self.onUpdate, size=(100, -1)) 

683 self.yrop = Choice(panel, choices=ARR_OPS, action=self.onUpdate, size=(100, -1)) 

684 

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

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

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

688 self.yrpop.SetStringSelection(self.config['yrpop']) 

689 self.yrop.SetStringSelection(self.config['yrop']) 

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

691 self.monod_val.SetAction(self.onUpdate) 

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

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

694 self.yerr_op.SetStringSelection(self.config['yerr_op']) 

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

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

697 self.ysuf.SetLabel(')') 

698 

699 ixsel, iysel = 0, 1 

700 iy2sel = iyesel = iyr1sel = iyr2sel = len(yarr_labels)-1 

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

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

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

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

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

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

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

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

709 if self.config['yref1'] in self.array_labels: 

710 iyr1sel = self.array_labels.index(self.config['yref1']) 

711 if self.config['yref2'] in self.array_labels: 

712 iyr2sel = self.array_labels.index(self.config['yref2']) 

713 

714 self.xarr.SetSelection(ixsel) 

715 self.yarr1.SetSelection(iysel) 

716 self.yarr2.SetSelection(iy2sel) 

717 self.yerr_arr.SetSelection(iyesel) 

718 self.yref1.SetSelection(iyr1sel) 

719 self.yref2.SetSelection(iyr2sel) 

720 

721 self.wid_filename = wx.TextCtrl(panel, value=fix_filename(group.filename), 

722 size=(250, -1)) 

723 self.wid_groupname = wx.TextCtrl(panel, value=group.groupname, 

724 size=(150, -1)) 

725 if not edit_groupname: 

726 self.wid_groupname.Disable() 

727 self.wid_reffilename = wx.TextCtrl(panel, value=fix_filename(group.filename + '_ref'), 

728 size=(250, -1)) 

729 self.wid_refgroupname = wx.TextCtrl(panel, value=group.groupname + '_ref', 

730 size=(150, -1)) 

731 

732 self.onTransCheck(is_trans=self.config['is_trans']) 

733 self.onYrefCheck(has_yref=self.config['has_yref']) 

734 

735 

736 bpanel = wx.Panel(panel) 

737 bsizer = wx.BoxSizer(wx.HORIZONTAL) 

738 _ok = Button(bpanel, 'OK', action=self.onOK) 

739 _cancel = Button(bpanel, 'Cancel', action=self.onCancel) 

740 _edit = Button(bpanel, 'Edit Array Names', action=self.onEditNames) 

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

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

743 self.multi_clear.Disable() 

744 _edit.SetToolTip('Change the current Column Names') 

745 

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

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

748 bsizer.Add(_ok) 

749 bsizer.Add(_cancel) 

750 bsizer.Add(_edit) 

751 bsizer.Add(self.multi_sel) 

752 bsizer.Add(self.multi_clear) 

753 _ok.SetDefault() 

754 pack(bpanel, bsizer) 

755 

756 self.dtc_button = Button(panel, 'Sum and Correct Fluoresence Data', action=self.onDTC) 

757 self.dtc_button.SetToolTip('Select channels and do deadtime-corrections for multi-element fluorescence data') 

758 

759 sizer = wx.GridBagSizer(2, 2) 

760 sizer.Add(title, (0, 0), (1, 7), LEFT, 5) 

761 

762 ir = 1 

763 sizer.Add(subtitle(' X [Energy] Array:'), (ir, 0), (1, 2), LEFT, 0) 

764 sizer.Add(dtype_lab, (ir, 3), (1, 1), RIGHT, 0) 

765 sizer.Add(self.datatype, (ir, 4), (1, 2), LEFT, 0) 

766 

767 ir += 1 

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

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

770 sizer.Add(units_lab, (ir, 3), (1, 1), RIGHT, 0) 

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

772 

773 ir += 1 

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

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

776 

777 ir += 1 

778 sizer.Add(subtitle(' Y [\u03BC(E)] Array:'), (ir, 0), (1, 1), LEFT, 0) 

779 sizer.Add(self.is_trans, (ir, 1), (1, 2), LEFT, 0) 

780 sizer.Add(self.dtc_button, (ir, 3), (1, 2), RIGHT, 0) 

781 ir += 1 

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

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

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

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

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

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

788 

789 

790 ir += 1 

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

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

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

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

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

796 

797 ir += 1 

798 sizer.Add(SimpleText(panel, ' Display Name:'), (ir, 0), (1, 1), LEFT, 0) 

799 sizer.Add(self.wid_filename, (ir, 1), (1, 2), LEFT, 0) 

800 sizer.Add(SimpleText(panel, ' Group Name:'), (ir, 3), (1, 1), RIGHT, 0) 

801 sizer.Add(self.wid_groupname, (ir, 4), (1, 2), LEFT, 0) 

802 

803 ir += 1 

804 sizer.Add(self.info_message, (ir, 0), (1, 5), LEFT, 1) 

805 

806 ir += 2 

807 sizer.Add(subtitle(' Reference [\u03BC_ref(E)] Array: '), 

808 (ir, 0), (1, 2), LEFT, 0) 

809 sizer.Add(self.has_yref, (ir, 2), (1, 3), LEFT, 0) 

810 

811 ir += 1 

812 sizer.Add(refylab, (ir, 0), (1, 1), LEFT, 0) 

813 sizer.Add(self.yrpop, (ir, 1), (1, 1), LEFT, 0) 

814 sizer.Add(self.yref1, (ir, 2), (1, 1), LEFT, 0) 

815 sizer.Add(self.yrop, (ir, 3), (1, 1), RIGHT, 0) 

816 sizer.Add(self.yref2, (ir, 4), (1, 2), LEFT, 0) 

817 

818 ir += 1 

819 sizer.Add(SimpleText(panel, ' Reference Name:'), (ir, 0), (1, 1), LEFT, 0) 

820 sizer.Add(self.wid_reffilename, (ir, 1), (1, 2), LEFT, 0) 

821 sizer.Add(SimpleText(panel, ' Group Name:'), (ir, 3), (1, 1), RIGHT, 0) 

822 sizer.Add(self.wid_refgroupname, (ir, 4), (1, 2), LEFT, 0) 

823 

824 ir +=1 

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

826 

827 pack(panel, sizer) 

828 

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

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

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

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

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

834 

835 self.plotpanel = PlotPanel(self, messenger=self.plot_messages) 

836 try: 

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

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

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

840 except: 

841 pass 

842 

843 

844 self.plotpanel.SetMinSize((200, 200)) 

845 textpanel = wx.Panel(self) 

846 ftext = wx.TextCtrl(textpanel, style=wx.TE_MULTILINE|wx.TE_READONLY, 

847 size=(400, 250)) 

848 

849 ftext.SetValue(group.text) 

850 ftext.SetFont(Font(FONTSIZE)) 

851 

852 textsizer = wx.BoxSizer(wx.VERTICAL) 

853 textsizer.Add(ftext, 1, LEFT|wx.GROW, 1) 

854 pack(textpanel, textsizer) 

855 

856 self.nb.AddPage(textpanel, ' Text of Data File ', True) 

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

858 

859 mainsizer = wx.BoxSizer(wx.VERTICAL) 

860 mainsizer.Add(panel, 0, wx.GROW|wx.ALL, 2) 

861 mainsizer.Add(self.nb, 1, LEFT|wx.GROW, 2) 

862 pack(self, mainsizer) 

863 

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

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

866 statusbar_fields = [group.filename, ""] 

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

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

869 

870 self.set_energy_units() 

871 dtc_conf = self.config.get('dtc_config', {}) 

872 if len(dtc_conf) > 0: 

873 self.onDTC_OK(dtc_conf, update=False) 

874 

875 self.Show() 

876 self.Raise() 

877 self.onUpdate() 

878 

879 def onDTC(self, event=None): 

880 self.show_subframe('dtc_conf', DeadtimeCorrectionFrame, 

881 config=self.config['dtc_config'], 

882 group=self.workgroup, 

883 on_ok=self.onDTC_OK) 

884 

885 def onDTC_OK(self, config, update=True, **kws): 

886 label, sum = sum_fluor_channels(self.workgroup, config['roi'], 

887 icr=config['icr'], 

888 ocr=config['ocr'], 

889 ltime=config['ltime'], 

890 add_data=False) 

891 if sum is None: 

892 return 

893 self.info_message.SetLabel(f"Added array '{label}' with summed and corrected fluorecence data") 

894 self.workgroup.array_labels.append(label) 

895 self.set_array_labels(self.workgroup.array_labels) 

896 npts = len(sum) 

897 new = np.append(self.workgroup.raw.data, sum.reshape(1, npts), axis=0) 

898 self.workgroup.raw.data = new[()] 

899 self.workgroup.data = new[()] 

900 self.yarr1.SetStringSelection(label) 

901 self.config['dtc_config'] = config 

902 if update: 

903 self.onUpdate() 

904 

905 def onClearMultiColumn(self, event=None): 

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

907 self.info_message.SetLabel(f" cleared reading of multiple columns") 

908 self.multi_clear.Disable() 

909 self.yarr1.Enable() 

910 self.ypop.Enable() 

911 self.yop.Enable() 

912 self.onUpdate() 

913 

914 

915 def onMultiColumn(self, event=None): 

916 self.show_subframe('multicol', MultiColumnFrame, 

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

918 group=self.workgroup, 

919 on_ok=self.onMultiColumn_OK) 

920 

921 

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

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

924 if len(chans) == 0: 

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

926 else: 

927 self.config['multicol_config'] = config 

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

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

930 self.ypop.SetStringSelection('') 

931 self.yarr1.Disable() 

932 self.ypop.Disable() 

933 self.yop.Disable() 

934 y2 = self.yarr2.GetStringSelection() 

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

936 self.info_message.SetLabel(msg) 

937 self.multi_clear.Enable() 

938 if update: 

939 self.onUpdate() 

940 

941 

942 def read_column_file(self, path): 

943 """read column file, generally as initial read""" 

944 parent, filename = os.path.split(path) 

945 reader, text = guess_filereader(path, return_text=True) 

946 

947 if reader == 'read_specfile': 

948 if not is_specfile(path, require_multiple_scans=True): 

949 reader = 'read_ascii' 

950 

951 if reader in ('read_xdi', 'read_gsexdi'): 

952 # first check for Nans and Infs 

953 nan_result = look_for_nans(path) 

954 if 'read error' in nan_result.message: 

955 title = "Cannot read %s" % path 

956 message = "Error reading %s\n%s" %(path, nan_result.message) 

957 r = Popup(self.parent, message, title) 

958 return None 

959 if 'no data' in nan_result.message: 

960 title = "No data in %s" % path 

961 message = "No data found in file %s" % path 

962 r = Popup(self.parent, message, title) 

963 return None 

964 

965 if ('has nans' in nan_result.message or 

966 'has infs' in nan_result.message): 

967 reader = 'read_ascii' 

968 

969 tmpname = '_tmpfile_' 

970 read_cmd = "%s = %s('%s')" % (tmpname, reader, path) 

971 self.reader = reader 

972 _larch = self._larch 

973 

974 if (not isinstance(_larch, larch.Interpreter) and 

975 hasattr(_larch, '_larch')): 

976 _larch = _larch._larch 

977 try: 

978 _larch.eval(read_cmd, add_history=True) 

979 except: 

980 pass 

981 if len(_larch.error) > 0 and reader in ('read_xdi', 'read_gsexdi'): 

982 read_cmd = "%s = %s('%s')" % (tmpname, 'read_ascii', path) 

983 try: 

984 _larch.eval(read_cmd, add_history=True) 

985 except: 

986 pass 

987 if len(_larch.error) == 0: 

988 self.reader = 'read_ascii' 

989 

990 if len(_larch.error) > 0: 

991 msg = ["Error trying to read '%s':" % path, ""] 

992 for err in _larch.error: 

993 exc_name, errmsg = err.get_error() 

994 msg.append(errmsg) 

995 

996 title = "Cannot read %s" % path 

997 r = Popup(self.parent, "\n".join(msg), title) 

998 return None 

999 group = deepcopy(_larch.symtable.get_symbol(tmpname)) 

1000 _larch.symtable.del_symbol(tmpname) 

1001 

1002 group.text = text 

1003 group.path = path 

1004 group.filename = filename 

1005 group.groupname = file2groupname(filename, 

1006 symtable=self._larch.symtable) 

1007 return group 

1008 

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

1010 shown = False 

1011 if name in self.subframes: 

1012 try: 

1013 self.subframes[name].Raise() 

1014 shown = True 

1015 except: 

1016 pass 

1017 if not shown: 

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

1019 self.subframes[name].Show() 

1020 self.subframes[name].Raise() 

1021 

1022 

1023 def onEditNames(self, evt=None): 

1024 self.show_subframe('editcol', EditColumnFrame, 

1025 group=self.workgroup, 

1026 on_ok=self.set_array_labels) 

1027 

1028 def set_array_labels(self, arr_labels): 

1029 self.workgroup.array_labels = arr_labels 

1030 yarr_labels = self.yarr_labels = arr_labels + ['1.0', ''] 

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

1032 def update(wid, choices): 

1033 curstr = wid.GetStringSelection() 

1034 curind = wid.GetSelection() 

1035 wid.SetChoices(choices) 

1036 if curstr in choices: 

1037 wid.SetStringSelection(curstr) 

1038 else: 

1039 wid.SetSelection(curind) 

1040 update(self.xarr, xarr_labels) 

1041 update(self.yarr1, yarr_labels) 

1042 update(self.yarr2, yarr_labels) 

1043 update(self.yerr_arr, yarr_labels) 

1044 self.onUpdate() 

1045 

1046 def onOK(self, event=None): 

1047 """ build arrays according to selection """ 

1048 self.read_form() 

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

1050 self.config.update(cout) 

1051 conf = self.config 

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

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

1054 

1055 self.expressions = conf['expressions'] 

1056 filename = conf['user_filename'] 

1057 groupname = conf['groupname'] 

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

1059 

1060 # generate script to pass back to calling program: 

1061 labstr = ', '.join(self.array_labels) 

1062 buff = [f"{{group}} = {self.reader}('{{path}}', labels='{labstr}')", 

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

1064 "{group}.is_frozen = False", 

1065 "{group}.energy_ref = '{group}'"] 

1066 

1067 dtc_conf = conf.get('dtc_config', {}) 

1068 if len(dtc_conf) > 0: 

1069 sumcmd = "sum_fluor_channels({{group}}, {roi}, icr={icr}, ocr={ocr}, ltime={ltime})" 

1070 buff.append(sumcmd.format(**dtc_conf)) 

1071 

1072 buff.append("{group}.datatype = '%s'" % (conf['datatype'])) 

1073 

1074 for attr in ('plot_xlabel', 'plot_ylabel'): 

1075 val = getattr(self.workgroup, attr) 

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

1077 

1078 xexpr = self.expressions['xdat'] 

1079 en_units = conf['en_units'] 

1080 if en_units.startswith('deg'): 

1081 monod = conf['monod'] 

1082 buff.append(f"monod = {monod:.9f}") 

1083 buff.append(f"{{group}}.xdat = PLANCK_HC/(2*monod*sin(DEG2RAD*({xexpr:s})))") 

1084 elif en_units.startswith('keV'): 

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

1086 else: 

1087 buff.append(f"{{group}}.xdat = {xexpr:s}") 

1088 

1089 for aname in ('ydat', 'yerr'): 

1090 expr = self.expressions[aname] 

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

1092 

1093 if getattr(self.workgroup, 'datatype', 'raw') == 'xas': 

1094 if self.reader == 'read_gsescan': 

1095 buff.append("{group}.xdat = {group}.x") 

1096 buff.append("{group}.energy = {group}.xdat") 

1097 buff.append("{group}.mu = {group}.ydat") 

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

1099 else: 

1100 buff.append("{group}.scale = 1./({group}.ydat.ptp()+1.e-15)") 

1101 

1102 array_desc = dict(xdat=self.workgroup.plot_xlabel, 

1103 ydat=self.workgroup.plot_ylabel, 

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

1105 

1106 reffile = refgroup = None 

1107 if conf['has_yref']: 

1108 reffile = conf['reffile'] 

1109 refgroup = conf['refgroup'] 

1110 refexpr = self.expressions['yref'] 

1111 array_desc['yref'] = getattr(self.workgroup, 'yrlabel', 'reference') 

1112 

1113 buff.append("# reference group") 

1114 buff.append("{refgroup} = deepcopy({group})") 

1115 buff.append(f"{{refgroup}}.ydat = {{refgroup}}.mu = {refexpr}") 

1116 buff.append(f"{{refgroup}}.plot_ylabel = '{self.workgroup.yrlabel}'") 

1117 buff.append("{refgroup}.energy_ref = {group}.energy_ref = '{refgroup}'") 

1118 buff.append("# end reference group") 

1119 

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

1121 conf['array_desc'] = array_desc 

1122 

1123 if self.read_ok_cb is not None: 

1124 self.read_ok_cb(script, self.path, conf) 

1125 

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

1127 try: 

1128 f.Destroy() 

1129 except: 

1130 pass 

1131 self.Destroy() 

1132 

1133 def onCancel(self, event=None): 

1134 self.workgroup.import_ok = False 

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

1136 try: 

1137 f.Destroy() 

1138 except: 

1139 pass 

1140 self.Destroy() 

1141 

1142 def onYerrChoice(self, evt=None): 

1143 yerr_choice = evt.GetString() 

1144 self.yerr_arr.Disable() 

1145 self.yerr_val.Disable() 

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

1147 self.yerr_val.Enable() 

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

1149 self.yerr_arr.Enable() 

1150 # self.onUpdate() 

1151 

1152 def onTransCheck(self, evt=None, is_trans=False): 

1153 if evt is not None: 

1154 is_trans = evt.IsChecked() 

1155 if is_trans: 

1156 self.ypop.SetStringSelection('-log(') 

1157 else: 

1158 self.ypop.SetStringSelection('') 

1159 try: 

1160 self.onUpdate() 

1161 except: 

1162 pass 

1163 

1164 def onYrefCheck(self, evt=None, has_yref=False): 

1165 if evt is not None: 

1166 has_yref = evt.IsChecked() 

1167 self.yref1.Enable(has_yref) 

1168 self.yref2.Enable(has_yref) 

1169 self.yrpop.Enable(has_yref) 

1170 self.yrop.Enable(has_yref) 

1171 self.wid_reffilename.Enable(has_yref) 

1172 self.wid_refgroupname.Enable(has_yref) 

1173 

1174 

1175 def onXSelect(self, evt=None): 

1176 ix = self.xarr.GetSelection() 

1177 xname = self.xarr.GetStringSelection() 

1178 

1179 workgroup = self.workgroup 

1180 ncol, npts = self.workgroup.data.shape 

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

1182 workgroup.xdat = 1.0*np.arange(npts) 

1183 else: 

1184 workgroup.xdat = 1.0*self.workgroup.data[ix, :] 

1185 self.onUpdate() 

1186 

1187 self.monod_val.Disable() 

1188 if self.datatype.GetStringSelection().strip().lower() == 'raw': 

1189 self.en_units.SetSelection(4) 

1190 else: 

1191 eguess = guess_energy_units(workgroup.xdat) 

1192 if eguess.startswith('keV'): 

1193 self.en_units.SetSelection(1) 

1194 elif eguess.startswith('deg'): 

1195 self.en_units.SetSelection(2) 

1196 self.monod_val.Enable() 

1197 else: 

1198 self.en_units.SetSelection(0) 

1199 

1200 def onEnUnitsSelect(self, evt=None): 

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

1202 self.onUpdate() 

1203 

1204 def set_energy_units(self): 

1205 ix = self.xarr.GetSelection() 

1206 xname = self.xarr.GetStringSelection() 

1207 workgroup = self.workgroup 

1208 ncol, npts = workgroup.data.shape 

1209 

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

1211 workgroup.xdat = 1.0*np.arange(npts) 

1212 else: 

1213 workgroup.xdat = 1.0*self.workgroup.data[ix, :] 

1214 if self.datatype.GetStringSelection().strip().lower() != 'raw': 

1215 eguess = guess_energy_units(workgroup.xdat) 

1216 if eguess.startswith('eV'): 

1217 self.en_units.SetStringSelection('eV') 

1218 elif eguess.startswith('keV'): 

1219 self.en_units.SetStringSelection('keV') 

1220 

1221 def read_form(self, **kws): 

1222 """return form configuration""" 

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

1224 if self.workgroup.datatype == 'raw' and datatype == 'xas': 

1225 self.workgroup.datatype = 'xas' 

1226 eguess = guess_energy_units(self.workgroup.xdat) 

1227 if eguess.startswith('keV'): 

1228 self.en_units.SetSelection(1) 

1229 elif eguess.startswith('deg'): 

1230 self.en_units.SetSelection(2) 

1231 self.monod_val.Enable() 

1232 else: 

1233 self.en_units.SetSelection(0) 

1234 if datatype == 'raw': 

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

1236 

1237 conf = {'datatype': datatype, 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1252 'has_yref': self.has_yref.IsChecked(), 

1253 'yref1': self.yref1.GetStringSelection().strip(), 

1254 'yref2': self.yref2.GetStringSelection().strip(), 

1255 'iry1': self.yref1.GetSelection(), 

1256 'iry2': self.yref2.GetSelection(), 

1257 'yrpop': self.yrpop.GetStringSelection().strip(), 

1258 'yrop': self.yop.GetStringSelection().strip(), 

1259 'user_filename': self.wid_filename.GetValue(), 

1260 'groupname': fix_varname(self.wid_groupname.GetValue()), 

1261 'reffile': self.wid_reffilename.GetValue(), 

1262 'refgroup': fix_varname(self.wid_refgroupname.GetValue()), 

1263 } 

1264 self.config.update(conf) 

1265 return conf 

1266 

1267 def onUpdate(self, evt=None, **kws): 

1268 """column selections changed calc xdat and ydat""" 

1269 workgroup = self.workgroup 

1270 try: 

1271 ncol, npts = self.workgroup.data.shape 

1272 except: 

1273 return 

1274 

1275 conf = self.read_form() 

1276 cout = create_arrays(workgroup, **conf) 

1277 self.expressions = cout.pop('expressions') 

1278 conf.update(cout) 

1279 

1280 if energy_may_need_rebinning(workgroup): 

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

1282 

1283 path, fname = os.path.split(workgroup.filename) 

1284 popts = dict(marker='o', markersize=4, linewidth=1.5, title=fname, 

1285 xlabel=workgroup.plot_xlabel, 

1286 ylabel=workgroup.plot_ylabel, 

1287 label=workgroup.plot_ylabel) 

1288 

1289 self.plotpanel.plot(workgroup.xdat, workgroup.ydat, **popts) 

1290 if conf['has_yref']: 

1291 yrlabel = getattr(workgroup, 'plot_yrlabel', 'reference') 

1292 self.plotpanel.oplot(workgroup.xdat, workgroup.yref, 

1293 y2label=yrlabel, 

1294 linewidth=2.0, color='#E08070', 

1295 label=yrlabel, zorder=-40, side='right') 

1296 

1297 for i in range(self.nb.GetPageCount()): 

1298 if 'plot' in self.nb.GetPageText(i).lower(): 

1299 self.nb.SetSelection(i) 

1300 

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

1302 self.statusbar.SetStatusText(msg, panel) 

1303 

1304 

1305def create_arrays(dgroup, datatype='xas', ix=0, xarr='energy', en_units='eV', 

1306 monod=3.1355316, yarr1=None, yarr2=None, iy1=2, iy2=1, yop='/', 

1307 ypop='', iyerr=5, yerr_arr=None, yerr_op='constant', yerr_val=1.0, 

1308 has_yref=False, yref1=None, yref2=None, iry1=3, iry2=2, 

1309 yrpop='', yrop='/', **kws): 

1310 """ 

1311 build arrays and values for datagroup based on configuration as from ColumnFile 

1312 """ 

1313 ncol, npts = dgroup.data.shape 

1314 exprs = dict(xdat=None, ydat=None, yerr=None, yref=None) 

1315 

1316 # print("CREATE ARRAYS ", dgroup, datatype, ncol, npts) 

1317 if not hasattr(dgroup, 'index'): 

1318 dgroup.index = 1.0*np.arange(npts) 

1319 

1320 if xarr.startswith('_index') or ix >= ncol: 

1321 dgroup.xdat = 1.0*np.arange(npts) 

1322 xarr = '_index' 

1323 exprs['xdat'] = 'arange({npts})' 

1324 else: 

1325 dgroup.xdat = 1.0*dgroup.data[ix, :] 

1326 exprs['xdat'] = '{group}.data[{ix}, : ]' 

1327 

1328 xlabel = xarr 

1329 monod = float(monod) 

1330 if en_units.startswith('deg'): 

1331 dgroup.xdat = PLANCK_HC/(2*monod*np.sin(DEG2RAD*dgroup.xdat)) 

1332 xlabel = xarr + ' (eV)' 

1333 elif en_units.startswith('keV'): 

1334 dgroup.xdat *= 1000.0 

1335 xlabel = xarr + ' (eV)' 

1336 

1337 def pre_op(opstr, arr): 

1338 if opstr == '-': 

1339 return '', opstr, -arr 

1340 suf = '' 

1341 if opstr in ('-log(', 'log('): 

1342 suf = ')' 

1343 arr = safe_log(arr) 

1344 if opstr.startswith('-'): arr = -arr 

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

1346 return suf, opstr, arr 

1347 

1348 if yarr1 is None: 

1349 yarr1 = dgroup.array_labels[iy1] 

1350 

1351 if yarr2 is None: 

1352 yarr2 = dgroup.array_labels[iy2] 

1353 

1354 ylabel = yarr1 

1355 if len(yarr2) == 0: 

1356 yarr2 = '1.0' 

1357 else: 

1358 ylabel = f"{ylabel}{yop}{yarr2}" 

1359 

1360 if yarr1 == '0.0': 

1361 ydarr1 = np.zeros(npts)*1.0 

1362 yexpr1 = f'np.zeros(npts)' 

1363 elif len(yarr1) == 0 or yarr1 == '1.0' or iy1 >= ncol: 

1364 ydarr1 = np.ones(npts)*1.0 

1365 yexpr1 = f'np.ones({npts})' 

1366 else: 

1367 ydarr1 = dgroup.data[iy1, :] 

1368 yexpr1 = '{group}.data[{iy1}, : ]' 

1369 

1370 dgroup.ydat = ydarr1 

1371 exprs['ydat'] = yexpr1 

1372 

1373 if yarr2 == '0.0': 

1374 ydarr2 = np.zeros(npts)*1.0 

1375 yexpr2 = '0.0' 

1376 elif len(yarr2) == 0 or yarr2 == '1.0' or iy2 >= ncol: 

1377 ydarr2 = np.ones(npts)*1.0 

1378 yexpr2 = '1.0' 

1379 else: 

1380 ydarr2 = dgroup.data[iy2, :] 

1381 yexpr2 = '{group}.data[{iy2}, : ]' 

1382 

1383 if yop in ('+', '-', '*', '/'): 

1384 exprs['ydat'] = f"{yexpr1}{yop}{yexpr2}" 

1385 if yop == '+': 

1386 dgroup.ydat = ydarr1 + ydarr2 

1387 elif yop == '-': 

1388 dgroup.ydat = ydarr1 - ydarr2 

1389 elif yop == '*': 

1390 dgroup.ydat = ydarr1 * ydarr2 

1391 elif yop == '/': 

1392 dgroup.ydat = ydarr1 / ydarr2 

1393 

1394 ysuf, ypop, dgroup.ydat = pre_op(ypop, dgroup.ydat) 

1395 ypopx = ypop.replace('log', 'safe_log') 

1396 exprs['ydat'] = f"{ypopx}{exprs['ydat']}{ysuf}" 

1397 ylabel = f"{ypop}{ylabel}{ysuf}" 

1398 

1399 # error 

1400 exprs['yerr'] = '1' 

1401 if yerr_op.startswith('const'): 

1402 yderr = yerr_val 

1403 exprs['yerr'] = f"{yerr_val}" 

1404 elif yerr_op.startswith('array'): 

1405 yderr = dgroup.data[iyerr, :] 

1406 exprs['yerr'] = '{group}.data[{iyerr}, :]' 

1407 elif yerr_op.startswith('sqrt'): 

1408 yderr = np.sqrt(dgroup.ydat) 

1409 exprs['yerr'] = 'sqrt({group}.ydat)' 

1410 

1411 # reference 

1412 yrlabel = None 

1413 if has_yref: 

1414 yrlabel = yref1 

1415 if len(yref2) == 0: 

1416 yref2 = '1.0' 

1417 else: 

1418 yrlabel = f"{yrlabel}{yrop}{yref2}" 

1419 

1420 if yref1 == '0.0': 

1421 ydrarr1 = np.zeros(npts)*1.0 

1422 yrexpr1 = 'zeros({npts})' 

1423 elif len(yref1) == 0 or yref1 == '1.0' or iry1 >= ncol: 

1424 ydrarr1 = np.ones(npts)*1.0 

1425 yrexpr1 = 'ones({npts})' 

1426 else: 

1427 ydrarr1 = dgroup.data[iry1, :] 

1428 yrexpr1 = '{group}.data[{iry1}, : ]' 

1429 

1430 dgroup.yref = ydrarr1 

1431 exprs['yref'] = yrexpr1 

1432 

1433 if yref2 == '0.0': 

1434 ydrarr2 = np.zeros(npts)*1.0 

1435 ydrexpr2 = '0.0' 

1436 elif len(yref2) == 0 or yref2 == '1.0' or iry2 >= ncol: 

1437 ydrarr2 = np.ones(npts)*1.0 

1438 yrexpr2 = '1.0' 

1439 else: 

1440 ydrarr2 = dgroup.data[iry2, :] 

1441 yrexpr2 = '{group}.data[{iry2}, : ]' 

1442 

1443 if yrop in ('+', '-', '*', '/'): 

1444 exprs['yref'] = f'{yrexpr1} {yop} {yrexpr2}' 

1445 if yrop == '+': 

1446 dgroup.yref = ydrarr1 + ydrarr2 

1447 elif yrop == '-': 

1448 dgroup.yref = ydrarr1 - ydrarr2 

1449 elif yrop == '*': 

1450 dgroup.yref = ydrarr1 * ydarr2 

1451 elif yrop == '/': 

1452 dgroup.yref = ydrarr1 / ydrarr2 

1453 

1454 yrsuf, yprop, dgroup.yref = pre_op(yrpop, dgroup.yref) 

1455 yrpopx = yrpop.replace('log', 'safe_log') 

1456 exprs['yref'] = f"{yrpopx}{exprs['yref']}{yrsuf}" 

1457 yrlabel = f'{yrpop} {yrlabel} {yrsuf}' 

1458 dgroup.yrlabel = yrlabel 

1459 

1460 

1461 try: 

1462 npts = min(len(dgroup.xdat), len(dgroup.ydat)) 

1463 except AttributeError: 

1464 return 

1465 except ValueError: 

1466 return 

1467 

1468 en = dgroup.xdat 

1469 dgroup.datatype = datatype 

1470 dgroup.npts = npts 

1471 dgroup.plot_xlabel = xlabel 

1472 dgroup.plot_ylabel = ylabel 

1473 dgroup.xdat = np.array(dgroup.xdat[:npts]) 

1474 dgroup.ydat = np.array(dgroup.ydat[:npts]) 

1475 dgroup.y = dgroup.ydat 

1476 dgroup.yerr = yderr 

1477 if isinstance(yderr, np.ndarray): 

1478 dgroup.yerr = np.array(yderr[:npts]) 

1479 if yrlabel is not None: 

1480 dgroup.plot_yrlabel = yrlabel 

1481 

1482 if dgroup.datatype == 'xas': 

1483 dgroup.energy = dgroup.xdat 

1484 dgroup.mu = dgroup.ydat 

1485 

1486 return dict(xarr=xarr, ypop=ypop, yop=yop, yarr1=yarr1, yarr2=yarr2, 

1487 monod=monod, en_units=en_units, yerr_op=yerr_op, 

1488 yerr_val=yerr_val, yerr_arr=yerr_arr, yrpop=yrpop, yrop=yrop, 

1489 yref1=yref1, yref2=yref2, has_yref=has_yref, 

1490 expressions=exprs) 

1491 

1492def energy_may_need_rebinning(workgroup): 

1493 "test if energy may need rebinning" 

1494 if getattr(workgroup, 'datatype', '?') != 'xas': 

1495 return False 

1496 en = getattr(workgroup, 'xdat', [-8.0e12]) 

1497 if len(en) < 2: 

1498 return False 

1499 if not isinstance(en, np.ndarray): 

1500 en = np.array(en) 

1501 if len(en) > 2000 or any(np.diff(en))< 0: 

1502 return True 

1503 if (len(en) > 200 and (max(en) - min(en)) > 350 and 

1504 np.diff(en[:-100]).mean() < 1.0): 

1505 return True