Coverage for /Users/Newville/Codes/xraylarch/larch/wxlib/feff_browser.py: 15%

423 statements  

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

1import os 

2import sys 

3import time 

4import logging 

5import shutil 

6from datetime import datetime, timedelta 

7import wx 

8import wx.lib.scrolledpanel as scrolled 

9import wx.dataview as dv 

10 

11import larch 

12from larch.site_config import user_larchdir 

13from larch.utils import unixpath, mkdir, read_textfile 

14from larch.wxlib import (GridPanel, GUIColors, Button, pack, SimpleText, 

15 FileOpen, FileSave, Font, LEFT, FRAMESTYLE, 

16 FONTSIZE, MenuItem, EditableListBox, OkCancel, 

17 FileCheckList, Choice, HLine, ReportFrame, Popup, 

18 LarchWxApp) 

19 

20from larch.xafs import get_feff_pathinfo 

21from larch.utils.physical_constants import ATOM_SYMS 

22 

23ATSYMS = ['< All Atoms>'] + ATOM_SYMS[:96] 

24EDGES = ['< All Edges>', 'K', 'L3', 'L2', 'L1', 'M5'] 

25 

26 

27LEFT = LEFT|wx.ALL 

28DVSTYLE = dv.DV_VERT_RULES|dv.DV_ROW_LINES|dv.DV_MULTIPLE 

29 

30class FeffPathsModel(dv.DataViewIndexListModel): 

31 def __init__(self, feffpaths, with_use=True): 

32 dv.DataViewIndexListModel.__init__(self, 0) 

33 self.data = [] 

34 self.paths = {} 

35 self.with_use = with_use 

36 self.feffpaths = feffpaths 

37 self.read_data() 

38 

39 def set_data(self, feffpaths): 

40 self.paths = {} 

41 self.feffpaths = feffpaths 

42 self.read_data() 

43 

44 def read_data(self): 

45 self.data = [] 

46 if self.feffpaths is None: 

47 row = ['feffNNNN.dat', '0.0000', '2', '6', '100.0'] 

48 if self.with_use: row.append(False) 

49 row.append('* -> * -> *') 

50 self.data.append(row) 

51 else: 

52 for fp in self.feffpaths: 

53 row = [fp.filename, '%.4f' % fp.reff, 

54 '%.0f' % fp.nleg, '%.0f' % fp.degen, 

55 '%.3f' % fp.cwratio] 

56 use = False 

57 if self.with_use: 

58 if fp.filename in self.paths: 

59 use = self.paths[fp.filename] 

60 row.append(use) 

61 row.append(fp.geom) 

62 self.data.append(row) 

63 self.paths[fp.filename] = use 

64 self.Reset(len(self.data)) 

65 

66 

67 def select_all(self, use=True): 

68 for pname in self.paths: 

69 self.paths[pname] = use 

70 self.read_data() 

71 

72 def select_above(self, item): 

73 itemname = self.GetValue(item, 0) 

74 use = True 

75 for row in self.data: 

76 self.paths[row[0]] = use 

77 if row[0] == itemname: 

78 use = not use 

79 self.read_data() 

80 

81 def GetColumnType(self, col): 

82 if self.with_use and col == 5: 

83 return "bool" 

84 return "string" 

85 

86 def GetValueByRow(self, row, col): 

87 return self.data[row][col] 

88 

89 def SetValueByRow(self, value, row, col): 

90 

91 self.data[row][col] = value 

92 return True 

93 

94 def GetColumnCount(self): 

95 return len(self.data[0]) 

96 

97 def GetCount(self): 

98 return len(self.data) 

99 

100 def GetAttrByRow(self, row, col, attr): 

101 """set row/col attributes (color, etc)""" 

102 nleg = self.data[row][2] 

103 cname = self.data[row][0] 

104 if nleg == '2': 

105 attr.SetColour('#000') 

106 attr.SetBold(False) 

107 return True 

108 elif nleg == '3': 

109 attr.SetColour('#A11') 

110 attr.SetBold(False) 

111 return True 

112 elif nleg == '4': 

113 attr.SetColour('#11A') 

114 attr.SetBold(False) 

115 return True 

116 else: 

117 attr.SetColour('#393') 

118 attr.SetBold(False) 

119 return True 

120 return False 

121 

122 

123class RemoveFeffCalcDialog(wx.Dialog): 

124 """dialog for removing Feff Calculations""" 

125 

126 def __init__(self, parent, ncalcs=1, **kws): 

127 title = "Remove Feff calculations?" 

128 wx.Dialog.__init__(self, parent, wx.ID_ANY, title=title, size=(325, 275)) 

129 panel = GridPanel(self, ncols=3, nrows=4, pad=2, itemstyle=LEFT) 

130 

131 panel.Add(SimpleText(panel, f'Remove {ncalcs:d} Feff calculations?'), 

132 dcol=3, newrow=True) 

133 panel.Add(SimpleText(panel, 'Warning: this cannot be undone!'), 

134 dcol=3, newrow=True) 

135 panel.Add((5, 5), newrow=True) 

136 panel.Add(HLine(panel, size=(500, 3)), dcol=2, newrow=True) 

137 panel.Add(OkCancel(panel), dcol=2, newrow=True) 

138 panel.pack() 

139 

140 def GetResponse(self): 

141 self.Raise() 

142 return (self.ShowModal() == wx.ID_OK) 

143 

144class FeffResultsPanel(wx.Panel): 

145 """ present Feff results """ 

146 def __init__(self, parent=None, feffresult=None, path_importer=None, 

147 _larch=None): 

148 wx.Panel.__init__(self, parent, -1, size=(700, 500)) 

149 self.parent = parent 

150 self.path_importer = path_importer 

151 self._larch = _larch 

152 self.feffresult = feffresult 

153 self.report_frame = None 

154 

155 self.dvc = dv.DataViewCtrl(self, style=DVSTYLE) 

156 self.dvc.SetMinSize((695, 350)) 

157 

158 self.model = FeffPathsModel(None, with_use=callable(path_importer)) 

159 self.dvc.AssociateModel(self.model) 

160 

161 panel = wx.Panel(self) 

162 # panel.SetBackgroundColour(GUIColors.bg) 

163 

164 sizer = wx.GridBagSizer(1, 1) 

165 

166 bkws = dict(size=(175, -1)) 

167 btn_header = Button(panel, "Show Full Header", action=self.onShowHeader, **bkws) 

168 btn_feffinp = Button(panel, "Show Feff.inp", action=self.onShowFeffInp, **bkws) 

169 btn_geom = Button(panel, "Show Path Geometries", action=self.onShowGeom, **bkws) 

170 

171 if callable(self.path_importer): 

172 btn_import = Button(panel, "Import Paths", action=self.onImportPath, **bkws) 

173 btn_above = Button(panel, "Select All Above Current", action=self.onSelAbove, **bkws) 

174 btn_none = Button(panel, "Select None", action=self.onSelNone, **bkws) 

175 

176 opts = dict(size=(475, -1), style=LEFT) 

177 self.feff_folder = SimpleText(panel, '', **opts) 

178 self.feff_datetime = SimpleText(panel, '',**opts) 

179 self.feff_header = [SimpleText(panel, '', **opts), 

180 SimpleText(panel, '', **opts), 

181 SimpleText(panel, '', **opts), 

182 SimpleText(panel, '', **opts), 

183 SimpleText(panel, '', **opts), 

184 SimpleText(panel, '', **opts)] 

185 

186 

187 ir = 0 

188 sizer.Add(SimpleText(panel, 'Feff Folder:'), (ir, 0), (1, 1), LEFT, 2) 

189 sizer.Add(self.feff_folder, (ir, 1), (1, 4), LEFT, 2) 

190 ir += 1 

191 sizer.Add(SimpleText(panel, 'Date Run:'), (ir, 0), (1, 1), LEFT, 2) 

192 sizer.Add(self.feff_datetime, (ir, 1), (1, 5), LEFT, 2) 

193 

194 ir += 1 

195 sizer.Add(SimpleText(panel, 'Header:'), (ir, 0), (1, 1), LEFT, 1) 

196 sizer.Add(self.feff_header[0], (ir, 1), (1, 5), LEFT, 1) 

197 ir += 1 

198 sizer.Add(SimpleText(panel, ''), (ir, 0), (1, 1), LEFT, 1) 

199 sizer.Add(self.feff_header[1], (ir, 1), (1, 5), LEFT, 1) 

200 ir += 1 

201 sizer.Add(SimpleText(panel, ''), (ir, 0), (1, 1), LEFT, 1) 

202 sizer.Add(self.feff_header[2], (ir, 1), (1, 5), LEFT, 1) 

203 ir += 1 

204 sizer.Add(SimpleText(panel, ''), (ir, 0), (1, 1), LEFT, 1) 

205 sizer.Add(self.feff_header[3], (ir, 1), (1, 5), LEFT, 1) 

206 ir += 1 

207 sizer.Add(SimpleText(panel, ''), (ir, 0), (1, 1), LEFT, 1) 

208 sizer.Add(self.feff_header[4], (ir, 1), (1, 5), LEFT, 1) 

209 ir += 1 

210 sizer.Add(SimpleText(panel, ''), (ir, 0), (1, 1), LEFT, 1) 

211 sizer.Add(self.feff_header[5], (ir, 1), (1, 5), LEFT, 1) 

212 

213 ir += 1 

214 sizer.Add(btn_header, (ir, 0), (1, 2), LEFT, 2) 

215 sizer.Add(btn_feffinp, (ir, 2), (1, 2), LEFT, 2) 

216 sizer.Add(btn_geom, (ir, 4), (1, 2), LEFT, 2) 

217 

218 if callable(self.path_importer): 

219 ir += 1 

220 sizer.Add(btn_above, (ir, 0), (1, 2), LEFT, 2) 

221 sizer.Add(btn_none, (ir, 2), (1, 2), LEFT, 2) 

222 sizer.Add(btn_import, (ir, 4), (1, 2), LEFT, 2) 

223 

224 ir += 1 

225 sizer.Add(wx.StaticLine(panel, size=(600, 2)),(ir, 0), (1, 6), LEFT, 2) 

226 

227 pack(panel, sizer) 

228 

229 columns = [('Feff File', 100, 'text'), 

230 ('R (\u212B)', 60, 'text'), 

231 ('# legs', 60, 'text'), 

232 ('# paths', 65, 'text'), 

233 ('Importance', 100, 'text')] 

234 if callable(self.path_importer): 

235 columns.append(('Use', 50, 'bool')) 

236 columns.append(('Geometry', 200, 'text')) 

237 

238 for icol, dat in enumerate(columns): 

239 label, width, dtype = dat 

240 method = self.dvc.AppendTextColumn 

241 mode = dv.DATAVIEW_CELL_EDITABLE 

242 if dtype == 'bool': 

243 method = self.dvc.AppendToggleColumn 

244 mode = dv.DATAVIEW_CELL_ACTIVATABLE 

245 method(label, icol, width=width, mode=mode) 

246 c = self.dvc.Columns[icol] 

247 align = wx.ALIGN_RIGHT 

248 if (label.startswith('Feff') or label.startswith('Geom')): 

249 align = wx.ALIGN_LEFT 

250 c.Alignment = c.Renderer.Alignment = align 

251 c.SetSortable(False) 

252 

253 

254 mainsizer = wx.BoxSizer(wx.VERTICAL) 

255 mainsizer.Add(panel, 0, LEFT, 1) 

256 mainsizer.Add(self.dvc, 0, LEFT, 1) 

257 

258 pack(self, mainsizer) 

259 self.dvc.EnsureVisible(self.model.GetItem(0)) 

260 

261 if feffresult is not None: 

262 self.set_feffresult(feffresult) 

263 

264 def onSelAll(self, event=None): 

265 self.model.select_all(True) 

266 

267 def onSelNone(self, event=None): 

268 self.model.select_all(False) 

269 

270 def onSelAbove(self, event=None): 

271 if self.dvc.HasSelection(): 

272 self.model.select_above(self.dvc.GetSelection()) 

273 

274 def onShowHeader(self, event=None): 

275 if self.feffresult is not None: 

276 self.show_report(self.feffresult.header, 

277 title=f'Header for {self.feffresult.folder:s}', 

278 default_filename=f'{self.feffresult.folder:s}_header.txt') 

279 

280 def onShowGeom(self, event=None): 

281 if self.feffresult is None: 

282 return 

283 show = False 

284 out = [] 

285 for data in self.model.data: 

286 if data[5]: 

287 show = True 

288 out.append(f'### {self.feffresult.folder:s}/{data[0]:s} ###') 

289 out.append('#Atom IPOT X Y Z Beta Eta Length') 

290 fname = data[0] 

291 

292 for fp in self.feffresult.paths: 

293 if fname == fp.filename: 

294 for i, px in enumerate(fp.geometry): 

295 at, ipot, r, x, y, z, beta, eta = px 

296 if i == 0: r = 0 

297 t = f'{at:4s} {ipot:3d} {x:9.4f} {y:9.4f} {z:9.4f} {beta:9.4f} {eta:9.4f} {r:9.4f}' 

298 out.append(t) 

299 if show: 

300 out = '\n'.join(out) 

301 self.show_report(out, title=f'Path Geometries for {self.feffresult.folder:s}', 

302 default_filename=f'{self.feffresult.folder:s}_paths.dat') 

303 

304 

305 

306 def onShowFeffInp(self, event=None): 

307 if self.feffresult is not None: 

308 text = None 

309 fname = unixpath(os.path.join(self.feffresult.folder, 'feff.inp')) 

310 if os.path.exists(fname): 

311 text = read_textfile(fname) 

312 else: 

313 fname = unixpath(os.path.join(user_larchdir, 'feff', 

314 self.feffresult.folder, 'feff.inp')) 

315 if os.path.exists(fname): 

316 text = read_textfile(fname) 

317 if text is not None: 

318 self.show_report(text, title=f'Feff.inp for {self.feffresult.folder:s}', 

319 default_filename=f'{self.feffresult.folder:s}_feff.inp', 

320 wildcard='Input Files (*.inp)|*.inp') 

321 

322 def show_report(self, text, title='Text', default_filename='out.txt', wildcard=None): 

323 if wildcard is None: 

324 wildcard='Text Files (*.txt)|*.txt' 

325 default_filename = os.path.split(default_filename)[1] 

326 try: 

327 self.report_frame.set_text(text) 

328 self.report_frame.SetTitle(title) 

329 self.report_frame.default_filename = default_filename 

330 self.report_frame.wildcard = wildcard 

331 except: 

332 self.report_frame = ReportFrame(parent=self, 

333 text=text, title=title, 

334 default_filename=default_filename, 

335 wildcard=wildcard) 

336 

337 

338 def onImportPath(self, event=None): 

339 folder = self.feffresult.folder 

340 _, fname = os.path.split(folder) 

341 for data in self.model.data: 

342 if data[5]: 

343 fname = data[0] 

344 fullpath = unixpath(os.path.join(folder, fname)) 

345 for pathinfo in self.feffresult.paths: 

346 if pathinfo.filename == fname: 

347 self.path_importer(fullpath, pathinfo) 

348 break 

349 

350 self.onSelNone() 

351 

352 

353 def set_feffresult(self, feffresult): 

354 self.feffresult = feffresult 

355 self.feff_folder.SetLabel(feffresult.folder) 

356 self.feff_datetime.SetLabel(feffresult.datetime) 

357 nhead = len(self.feff_header) 

358 

359 for i, text in enumerate(feffresult.header.split('\n')[:nhead]): 

360 self.feff_header[i].SetLabel(text) 

361 self.model.set_data(feffresult.paths) 

362 try: 

363 self.dvc.EnsureVisible(self.model.GetItem(0)) 

364 self.dvc.SetCurrentItem(self.dvc.GetTopItem()) 

365 except: 

366 pass 

367 

368 

369class FeffResultsFrame(wx.Frame): 

370 """ present Feff results """ 

371 def __init__(self, parent=None, feffresult=None, path_importer=None, _larch=None): 

372 wx.Frame.__init__(self, parent, -1, size=(900, 650), style=FRAMESTYLE) 

373 

374 title = "Manage Feff calculation results" 

375 self.larch = _larch 

376 if _larch is None: 

377 self.larch = larch.Interpreter() 

378 # self.larch.eval("# started Feff results browser\n") 

379 # self.larch.eval("if not hasattr('_sys', '_feffruns'): _sys._feffruns = {}") 

380 if not hasattr(self.larch.symtable._sys, '_feffruns'): 

381 self.larch.symtable._sys._feffruns = {} 

382 self.parent = parent 

383 

384 self.feff_folder = unixpath(os.path.join(user_larchdir, 'feff')) 

385 mkdir(self.feff_folder) 

386 

387 self.SetTitle(title) 

388 self.SetSize((925, 650)) 

389 self.SetFont(Font(FONTSIZE)) 

390 self.createMenus() 

391 

392 display0 = wx.Display(0) 

393 client_area = display0.ClientArea 

394 xmin, ymin, xmax, ymax = client_area 

395 xpos = int((xmax-xmin)*0.15) + xmin 

396 ypos = int((ymax-ymin)*0.20) + ymin 

397 self.SetPosition((xpos, ypos)) 

398 

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

400 splitter.SetMinimumPaneSize(250) 

401 

402 # left hand panel 

403 lpanel = wx.Panel(splitter) 

404 ltop = wx.Panel(lpanel) 

405 

406 def Btn(msg, x, act): 

407 b = Button(ltop, msg, size=(x, 30), action=act) 

408 b.SetFont(Font(FONTSIZE)) 

409 return b 

410 

411 sel_none = Btn('Select None', 120, self.onSelNone) 

412 sel_all = Btn('Select All', 120, self.onSelAll) 

413 tsizer = wx.BoxSizer(wx.HORIZONTAL) 

414 tsizer.Add(sel_all, 1, LEFT|wx.GROW, 1) 

415 tsizer.Add(sel_none, 1, LEFT|wx.GROW, 1) 

416 pack(ltop, tsizer) 

417 

418 self.fefflist = FileCheckList(lpanel, select_action=self.onShowFeff, 

419 size=(300, -1)) 

420 

421 lsizer = wx.BoxSizer(wx.VERTICAL) 

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

423 lsizer.Add(self.fefflist, 1, LEFT|wx.GROW|wx.ALL, 1) 

424 pack(lpanel, lsizer) 

425 

426 # right hand side 

427 panel = wx.Panel(splitter) ## scrolled.ScrolledPanel(splitter) 

428 wids = self.wids = {} 

429 toprow = wx.Panel(panel) 

430 

431 wids['central_atom'] = Choice(toprow, choices=ATSYMS, size=(125, -1), 

432 action=self.onCentralAtom) 

433 wids['edge'] = Choice(toprow, choices=EDGES, size=(125, -1), 

434 action=self.onAbsorbingEdge) 

435 

436 flabel = SimpleText(toprow, 'Filter Calculations by Element and Edge:', size=(175, -1)) 

437 tsizer = wx.BoxSizer(wx.HORIZONTAL) 

438 tsizer.Add(flabel, 0, LEFT, 2) 

439 tsizer.Add(wids['central_atom'], 0, LEFT|wx.GROW, 2) 

440 tsizer.Add(wids['edge'], 0, LEFT|wx.GROW, 2) 

441 pack(toprow, tsizer) 

442 

443 sizer = wx.BoxSizer(wx.VERTICAL) 

444 self.feff_panel = FeffResultsPanel(panel, path_importer=path_importer, 

445 _larch=_larch) 

446 sizer.Add(toprow, 0, LEFT|wx.GROW|wx.ALL, 2) 

447 sizer.Add(HLine(panel, size=(650, 2)), 0, LEFT|wx.GROW|wx.ALL, 2) 

448 sizer.Add(self.feff_panel, 1, LEFT|wx.GROW|wx.ALL, 2) 

449 pack(panel, sizer) 

450 # panel.SetupScrolling() 

451 splitter.SplitVertically(lpanel, panel, 1) 

452 self.Show() 

453 wx.CallAfter(self.onSearch) 

454 

455 def onShowFeff(self, event=None): 

456 fr = self.feffruns.get(self.fefflist.GetStringSelection(), None) 

457 if fr is not None: 

458 self.feff_panel.set_feffresult(fr) 

459 

460 

461 def onSearch(self, event=None): 

462 catom = self.wids['central_atom'].GetStringSelection() 

463 edge = self.wids['edge'].GetStringSelection() 

464 all_catoms = 'All' in catom 

465 all_edges = 'All' in edge 

466 

467 self.fefflist.Clear() 

468 self.feffruns = {} 

469 flist = os.listdir(self.feff_folder) 

470 flist = sorted(flist, key=lambda t: -os.stat(unixpath(os.path.join(self.feff_folder, t))).st_mtime) 

471 _feffruns = self.larch.symtable._sys._feffruns 

472 for path in flist: 

473 fullpath = unixpath(os.path.join(self.feff_folder, path)) 

474 if os.path.isdir(fullpath): 

475 try: 

476 _feffruns[path] = thisrun = get_feff_pathinfo(fullpath) 

477 if ((len(thisrun.paths) < 1) or 

478 (len(thisrun.ipots) < 1) or thisrun.shell is None): 

479 

480 self.larch.symtable._sys._feffruns.pop(path) 

481 else: 

482 self.feffruns[path] = thisrun 

483 if ((all_catoms or (thisrun.absorber == catom)) and 

484 (all_edges or (thisrun.shell == edge))): 

485 self.fefflist.Append(path) 

486 except: 

487 print(f"could not read Feff calculation from '{path}'") 

488 

489 def onCentralAtom(self, event=None): 

490 self.onSearch() 

491 

492 def onAbsorbingEdge(self, event=None): 

493 self.onSearch() 

494 

495 def onSelAll(self, event=None): 

496 self.fefflist.select_all() 

497 

498 def onSelNone(self, event=None): 

499 self.fefflist.select_none() 

500 

501 def onRemoveFeffFolders(self, event=None): 

502 dlg = RemoveFeffCalcDialog(self, ncalcs=len(self.fefflist.GetCheckedStrings())) 

503 dlg.Raise() 

504 dlg.SetWindowStyle(wx.STAY_ON_TOP) 

505 remove = dlg.GetResponse() 

506 dlg.Destroy() 

507 if remove: 

508 for checked in self.fefflist.GetCheckedStrings(): 

509 shutil.rmtree(unixpath(os.path.join(self.feff_folder, checked))) 

510 self.onSearch() 

511 

512 def onFeffFolder(self, event=None): 

513 "prompt for Feff Folder" 

514 dlg = wx.DirDialog(self, 'Select Main Folder for Feff Calculations', 

515 style=wx.DD_DEFAULT_STYLE|wx.DD_CHANGE_DIR) 

516 

517 dlg.SetPath(self.feff_folder) 

518 if dlg.ShowModal() == wx.ID_CANCEL: 

519 return None 

520 self.feff_folder = os.path.abspath(dlg.GetPath()) 

521 mkdir(self.feff_folder) 

522 

523 def onImportFeffCalc(self, event=None): 

524 "prompt to import Feff calculation folder" 

525 dlg = wx.DirDialog(self, 'Select Folder wth Feff Calculations', 

526 style=wx.DD_DEFAULT_STYLE|wx.DD_CHANGE_DIR) 

527 

528 dlg.SetPath(self.feff_folder) 

529 if dlg.ShowModal() == wx.ID_CANCEL: 

530 return None 

531 path = os.path.abspath(dlg.GetPath()) 

532 if os.path.exists(path): 

533 flist = os.listdir(path) 

534 if ('paths.dat' in flist and 'files.dat' in flist and 

535 'feff0001.dat' in flist and 'feff.inp' in flist): 

536 _, dname = os.path.split(path) 

537 dest = unixpath(os.path.join(self.feff_folder, dname)) 

538 shutil.copytree(path, dest) 

539 self.onSearch() 

540 else: 

541 Popup(self, f"{path:s} is not a complete Feff calculation", 

542 "cannot import Feff calculation") 

543 

544 def createMenus(self): 

545 # ppnl = self.plotpanel 

546 self.menubar = wx.MenuBar() 

547 fmenu = wx.Menu() 

548 

549 MenuItem(self, fmenu, "Rescan Main Feff Folder", 

550 "Rescan Feff Folder for Feff calculations", 

551 self.onSearch) 

552 

553 MenuItem(self, fmenu, "Import Feff calculation", 

554 "Import other Feff calculation", 

555 self.onImportFeffCalc) 

556 

557 fmenu.AppendSeparator() 

558 

559 MenuItem(self, fmenu, "Set Main Feff Folder", 

560 "Select Main Feff Folder for Feff calculations", 

561 self.onFeffFolder) 

562 

563 

564 MenuItem(self, fmenu, "Remove Selected Feff calculations", 

565 "Completely remove Feff calculations", self.onRemoveFeffFolders) 

566 

567 fmenu.AppendSeparator() 

568 MenuItem(self, fmenu, "Quit", "Exit", self.onClose) 

569 

570 self.menubar.Append(fmenu, "&File") 

571 self.SetMenuBar(self.menubar) 

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

573 

574 def onClose(self, event=None): 

575 self.Destroy() 

576 

577 

578class FeffResultsBrowserApp(LarchWxApp): 

579 def __init__(self, dat=None, **kws): 

580 self.dat = dat 

581 LarchWxApp.__init__(self, **kws) 

582 

583 def createApp(self): 

584 frame = FeffResultsFrame(feffresult=self.dat) 

585 self.SetTopWindow(frame) 

586 return True 

587 

588if __name__ == '__main__': 

589 dat = None 

590 FeffResultsBrowserApp(dat).MainLoop()