Coverage for /Users/Newville/Codes/xraylarch/larch/wxlib/larchframe.py: 16%

503 statements  

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

1#!/usr/bin/env python 

2# 

3 

4import sys 

5import os 

6import time 

7from functools import partial 

8import wx 

9import wx.lib.mixins.inspection 

10 

11import numpy 

12import scipy 

13import larch 

14from pyshortcuts import platform 

15 

16from wxutils import (MenuItem, Font, Button, Choice, panel_pack) 

17 

18from .gui_utils import LarchWxApp 

19from .readlinetextctrl import ReadlineTextCtrl 

20from .larchfilling import Filling 

21from .columnframe import ColumnDataFileFrame 

22from .athena_importer import AthenaImporter 

23from . import inputhook 

24 

25from larch.io import (read_ascii, read_xdi, read_gsexdi, 

26 gsescan_group, fix_varname, 

27 is_athena_project, AthenaProject) 

28from larch.version import make_banner, version_data 

29from larch.utils import get_cwd 

30 

31FILE_WILDCARDS = "Data Files(*.0*,*.dat,*.xdi)|*.0*;*.dat;*.xdi|All files (*.*)|*.*" 

32 

33ICON_FILE = 'larch.ico' 

34BACKGROUND_COLOUR = '#FCFCFA' 

35FOREGROUND_COLOUR = '#050520' 

36 

37FONTSIZE_FW = 14 

38if platform == 'win': 

39 FONTSIZE_FW = 12 

40elif platform == 'darwin': 

41 FONTSIZE_FW = 14 

42 

43def makeColorPanel(parent, color): 

44 p = wx.Panel(parent, -1) 

45 p.SetBackgroundColour(color) 

46 return p 

47 

48def wx_inspect(): 

49 wx.GetApp().ShowInspectionTool() 

50 

51def get_font(size): 

52 return wx.Font(size, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD) 

53 

54class LarchWxShell(object): 

55 ps1 = 'Larch>' 

56 ps2 = ' ... >' 

57 def __init__(self, wxparent=None, writer=None, _larch=None, 

58 prompt=None, historyfile=None, output=None, input=None): 

59 self._larch = _larch 

60 self.textstyle = None 

61 self.parent = wxparent 

62 self.prompt = prompt 

63 self.input = input 

64 self.output = output 

65 

66 if _larch is None: 

67 self._larch = larch.Interpreter(historyfile=historyfile, 

68 writer=self) 

69 self._larch.run_init_scripts() 

70 self.writer = self._larch.writer 

71 self.symtable = self._larch.symtable 

72 

73 self.objtree = wxparent.objtree 

74 

75 self.set_textstyle(mode='text') 

76 self._larch("_sys.display.colors['text2'] = {'color': 'blue'}", 

77 add_history=False) 

78 

79 self.symtable.set_symbol('_builtin.force_wxupdate', False) 

80 self.symtable.set_symbol('_sys.wx.inputhook', inputhook) 

81 self.symtable.set_symbol('_sys.wx.ping', inputhook.ping) 

82 self.symtable.set_symbol('_sys.wx.force_wxupdate', False) 

83 self.symtable.set_symbol('_sys.wx.wx_inspect', wx_inspect) 

84 self.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp()) 

85 self.symtable.set_symbol('_sys.wx.parent', wx.GetApp().GetTopWindow()) 

86 self.symtable.set_symbol('_sys.last_eval_time', 0.0) 

87 self.fontsize = FONTSIZE_FW 

88 

89 if self.output is not None: 

90 style = self.output.GetDefaultStyle() 

91 bgcol = style.GetBackgroundColour() 

92 sfont = style.GetFont() 

93 sfont.Family = wx.TELETYPE 

94 sfont.Weight = wx.BOLD 

95 sfont.PointSize = self.fontsize 

96 style.SetFont(sfont) 

97 self.output.SetDefaultStyle(style) 

98 self.textstyle = wx.TextAttr('black', bgcol, sfont) 

99 self.SetPrompt(True) 

100 

101 def onUpdate(self, event=None): 

102 symtable = self.symtable 

103 if symtable.get_symbol('_builtin.force_wxupdate', create=True): 

104 app = wx.GetApp() 

105 evtloop = wx.EventLoop() 

106 while evtloop.Pending(): 

107 evtloop.Dispatch() 

108 app.ProcessIdle() 

109 symtable.set_symbol('_builtin.force_wxupdate', False) 

110 

111 

112 def SetPrompt(self, complete): 

113 if self.prompt is None: 

114 return 

115 sprompt, scolor = self.ps1, '#000075' 

116 if not complete: 

117 sprompt, scolor = self.ps2, '#E00075' 

118 self.prompt.SetLabel(sprompt) 

119 self.prompt.SetForegroundColour(scolor) 

120 self.prompt.Refresh() 

121 

122 def set_textstyle(self, mode='text'): 

123 if self.output is None: 

124 return 

125 

126 display_colors = self.symtable._sys.display.colors 

127 

128 textattrs = display_colors.get(mode, {'color':'black'}) 

129 color = textattrs['color'] 

130 

131 style = self.output.GetDefaultStyle() 

132 bgcol = BACKGROUND_COLOUR 

133 sfont = self.output.GetFont() 

134 style.SetFont(sfont) 

135 self.output.SetDefaultStyle(style) 

136 self.textstyle = wx.TextAttr(color, bgcol, sfont) 

137 

138 def write_sys(self, text): 

139 sys.stdout.write(text) 

140 sys.stdout.flush() 

141 

142 def write(self, text, **kws): 

143 if text is None: 

144 return 

145 if self.textstyle is None: 

146 self.set_textstyle() 

147 

148 if self.output is None or self.textstyle is None: 

149 self.write_sys(text) 

150 else: 

151 self.output.SetInsertionPointEnd() 

152 pos0 = self.output.GetLastPosition() 

153 self.output.WriteText(text) 

154 pos1 = self.output.GetLastPosition() 

155 self.output.SetStyle(pos0, pos1, self.textstyle) 

156 self.output.SetInsertionPoint(pos1) 

157 self.output.Refresh() 

158 wx.CallAfter(self.input.SetFocus) 

159 

160 def flush(self, *args): 

161 self.output.Refresh() 

162 self.needs_flush = False 

163 

164 def clear_input(self): 

165 self._larch.input.clear() 

166 self.SetPrompt(True) 

167 

168 def onFlushTimer(self, event=None): 

169 if self.needs_flush: 

170 self.flush() 

171 

172 def eval(self, text, add_history=True, **kws): 

173 if text is None: 

174 return 

175 if text.startswith('!'): 

176 return os.system(text[1:]) 

177 

178 elif text.startswith('help(') and text.endswith(')'): 

179 topic = text[5:-1] 

180 parent = self.symtable.get_parentpath(topic) 

181 self.objtree.ShowNode("%s.%s" % (parent, topic)) 

182 return 

183 else: 

184 if add_history: 

185 self.parent.AddToHistory(text) 

186 self.write("%s\n" % text) 

187 ret = self._larch.eval(text, add_history=add_history) 

188 if self._larch.error: 

189 self._larch.input.clear() 

190 self._larch.writer.set_textstyle('error') 

191 self._larch.show_errors() 

192 self._larch.writer.set_textstyle('text') 

193 elif ret is not None: 

194 self._larch.writer.write("%s\n" % repr(ret)) 

195 try: 

196 self.objtree.onRefresh() 

197 except ValueError: 

198 pass 

199 self.symtable._sys.last_eval_time = time.time() 

200 self.SetPrompt(self._larch.input.complete) 

201 return ret 

202 

203class LarchPanel(wx.Panel): 

204 """Larch Input/Output Panel + Data Viewer as a wx.Panel, 

205 suitable for embedding into apps 

206 """ 

207 def __init__(self, parent=None, _larch=None, font=None, 

208 historyfile='history_larchgui.lar', **kwds): 

209 self.parent = parent 

210 if not historyfile.startswith(larch.site_config.user_larchdir): 

211 historyfile = os.path.join(larch.site_config.user_larchdir, 

212 historyfile) 

213 

214 wx.Panel.__init__(self, parent, -1, size=(750, 725)) 

215 

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

217 splitter.SetMinimumPaneSize(150) 

218 

219 self.objtree = Filling(splitter, rootLabel='_main', 

220 fgcol=FOREGROUND_COLOUR, bgcol=BACKGROUND_COLOUR) 

221 

222 self.output = wx.TextCtrl(splitter, -1, '', 

223 style=wx.TE_MULTILINE|wx.TE_RICH|wx.TE_READONLY) 

224 

225 self.output.SetBackgroundColour(BACKGROUND_COLOUR) 

226 self.output.SetForegroundColour(FOREGROUND_COLOUR) 

227 if font is None: 

228 font = get_font(self.fontsize) 

229 

230 self.output.SetFont(font) 

231 self.objtree.tree.SetFont(font) 

232 self.objtree.text.SetFont(font) 

233 

234 self.output.CanCopy() 

235 self.output.SetInsertionPointEnd() 

236 splitter.SplitHorizontally(self.objtree, self.output, 0) 

237 

238 ipanel = wx.Panel(self) 

239 

240 self.prompt = wx.StaticText(ipanel, label='Larch>', size=(75,-1), 

241 style=wx.ALIGN_CENTER|wx.ALIGN_RIGHT) 

242 

243 self.input = wx.TextCtrl(ipanel, value='', size=(525,-1), 

244 style=wx.TE_LEFT|wx.TE_PROCESS_ENTER) 

245 

246 self.input.Bind(wx.EVT_TEXT_ENTER, self.onText) 

247 if sys.platform == 'darwin': 

248 self.input.Bind(wx.EVT_KEY_UP, self.onChar) 

249 else: 

250 self.input.Bind(wx.EVT_CHAR, self.onChar) 

251 

252 self.hist_buff = [] 

253 self.hist_mark = 0 

254 

255 isizer = wx.BoxSizer(wx.HORIZONTAL) 

256 isizer.Add(self.prompt, 0, wx.BOTTOM|wx.CENTER) 

257 isizer.Add(self.input, 1, wx.ALIGN_LEFT|wx.EXPAND) 

258 

259 ipanel.SetSizer(isizer) 

260 isizer.Fit(ipanel) 

261 

262 opts = dict(flag=wx.ALL|wx.EXPAND, border=2) 

263 sizer = wx.BoxSizer(wx.VERTICAL) 

264 sizer.Add(splitter, 1, **opts) 

265 sizer.Add(ipanel, 0, **opts) 

266 

267 self.SetSizer(sizer) 

268 self.larchshell = LarchWxShell(wxparent=self, 

269 _larch = _larch, 

270 historyfile=historyfile, 

271 prompt = self.prompt, 

272 output = self.output, 

273 input = self.input) 

274 

275 self.objtree.SetRootObject(self.larchshell.symtable) 

276 # root = self.objtree.tree.GetRootItem() 

277 

278 

279 def write_banner(self): 

280 self.larchshell.set_textstyle('text2') 

281 self.larchshell.write(make_banner(show_libraries=['numpy', 'scipy', 'matplotlib', 'h5py', 

282 'lmfit', 'xraydb', 'wx','wxmplot'])) 

283 self.larchshell.write("\n \n") 

284 self.larchshell.set_textstyle('text') 

285 

286 def update(self): 

287 self.objtree.onRefresh() 

288 

289 def onText(self, event=None): 

290 text = event.GetString() 

291 self.input.Clear() 

292 if text.lower() in ('quit', 'exit', 'quit()', 'exit()'): 

293 if self.parent.exit_on_close: 

294 self.parent.onExit() 

295 else: 

296 wx.CallAfter(self.larchshell.eval, text) 

297 

298 def onChar(self, event=None): 

299 key = event.GetKeyCode() 

300 

301 entry = self.input.GetValue().strip() 

302 pos = self.input.GetSelection() 

303 ctrl = event.ControlDown() 

304 

305 if key == wx.WXK_RETURN and len(entry) > 0: 

306 pass 

307 if key in (wx.WXK_UP, wx.WXK_DOWN): 

308 if key == wx.WXK_UP: 

309 self.hist_mark = max(0, self.hist_mark-1) 

310 else: 

311 self.hist_mark += 1 

312 try: 

313 wx.CallAfter(self.set_input_text, self.hist_buff[self.hist_mark]) 

314 except IndexError: 

315 wx.CallAfter(self.set_input_text, '') 

316 event.Skip() 

317 

318 def set_input_text(self, text): 

319 self.input.SetValue(text) 

320 self.input.SetFocus() 

321 self.input.SetInsertionPointEnd() 

322 

323 

324 def AddToHistory(self, text=''): 

325 for tline in text.split('\n'): 

326 if len(tline.strip()) > 0: 

327 self.hist_buff.append(tline) 

328 self.hist_mark = len(self.hist_buff) 

329 

330 

331class LarchFrame(wx.Frame): 

332 def __init__(self, parent=None, _larch=None, is_standalone=True, 

333 historyfile='history_larchgui.lar', with_inspection=False, 

334 exit_on_close=False, with_raise=True, **kwds): 

335 

336 self.is_standalone = is_standalone 

337 self.with_inspection = with_inspection 

338 self.exit_on_close = exit_on_close 

339 self.parent = parent 

340 self.historyfile = historyfile 

341 self.subframes = {} 

342 self.last_array_sel = {} 

343 self.fontsize = FONTSIZE_FW 

344 

345 wx.Frame.__init__(self, parent, -1, size=(800, 725), 

346 style= wx.DEFAULT_FRAME_STYLE) 

347 self.SetTitle('LarchGUI') 

348 

349 self.font = get_font(self.fontsize) 

350 self.SetFont(self.font) 

351 sbar = self.CreateStatusBar(2, wx.CAPTION) 

352 

353 self.SetStatusWidths([-2,-1]) 

354 self.SetStatusText("Larch initializing...", 0) 

355 

356 self.mainpanel = LarchPanel(parent=self, _larch=_larch, 

357 historyfile=historyfile, 

358 font=self.font) 

359 

360 self.larchshell = self.mainpanel.larchshell 

361 self._larch = self.larchshell._larch 

362 

363 sizer = wx.BoxSizer(wx.VERTICAL) 

364 

365 sizer.Add(self.mainpanel, 1, wx.ALL|wx.EXPAND) 

366 

367 self.SetSizer(sizer) 

368 

369 self.Bind(wx.EVT_SHOW, self.onShow) 

370 self.BuildMenus() 

371 self.onSelectFont(fsize=self.fontsize) 

372 # larchdir = larch.site_config.larchdir 

373 

374 fico = os.path.join(larch.site_config.icondir, ICON_FILE) 

375 if os.path.exists(fico): 

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

377 self.mainpanel.write_banner() 

378 if with_raise: 

379 self.Raise() 

380 

381 def Raise(self): 

382 self.SetStatusText("Ready", 0) 

383 self.Refresh() 

384 wx.Frame.Raise(self) 

385 

386 def BuildMenus(self): 

387 menuBar = wx.MenuBar() 

388 

389 fmenu = wx.Menu() 

390 if self.is_standalone: 

391 MenuItem(self, fmenu, "&Read Data File\tCtrl+O", 

392 "Read Data File", self.onReadData) 

393 MenuItem(self, fmenu, "&Read and Run Larch Script\tCtrl+R", 

394 "Read and Execute a Larch Script", self.onRunScript) 

395 MenuItem(self, fmenu, "&Save Session History\tCtrl+S", 

396 "Save Session History to File", self.onSaveHistory) 

397 MenuItem(self, fmenu, 'Change Working Directory\tCtrl+W', 

398 'Change Directory', self.onChangeDir) 

399 MenuItem(self, fmenu, 'Clear Input\tCtrl+D', 

400 'Clear Input', self.onClearInput) 

401 

402 if self.with_inspection: 

403 MenuItem(self, fmenu, 'Show wxPython Inspector\tCtrl+I', 

404 'Debug wxPython App', self.onWxInspect) 

405 fmenu.AppendSeparator() 

406 

407 if self.parent is None and self.exit_on_close: 

408 self.Bind(wx.EVT_CLOSE, self.onExit) 

409 MenuItem(self, fmenu, 'E&xit', 'End program', 

410 self.onExit) 

411 else: 

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

413 MenuItem(self, fmenu, 'Close Display', 

414 'Close display', self.onClose) 

415 

416 menuBar.Append(fmenu, '&File') 

417 

418 _sys = self.larchshell.symtable._sys 

419 if self.is_standalone and hasattr(_sys, 'gui_apps'): 

420 appmenu = wx.Menu() 

421 x_apps = _sys.gui_apps.keys() 

422 for appname in sorted(x_apps): 

423 label, creator = _sys.gui_apps[appname] 

424 

425 MenuItem(self, appmenu, label, label, 

426 partial(self.show_subframe, 

427 name=appname, creator=creator)) 

428 menuBar.Append(appmenu, 'Applications') 

429 

430 fsmenu = wx.Menu() 

431 self.fontsizes = {} 

432 for fsize in (10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24): 

433 m = MenuItem(self, fsmenu, "%d" % fsize, "%d" % fsize, 

434 self.onSelectFont, kind=wx.ITEM_RADIO) 

435 self.fontsizes[m.GetId()] = fsize 

436 if fsize == self.fontsize: 

437 m.Check() 

438 

439 menuBar.Append(fsmenu, 'Font Size') 

440 

441 hmenu = wx.Menu() 

442 MenuItem(self, hmenu, '&About', 

443 'Information about this program', self.onAbout) 

444 MenuItem(self, hmenu, '&Versions', 

445 'Show versions of Larch and libraries', self.onVersions) 

446 menuBar.Append(hmenu, '&Help') 

447 self.SetMenuBar(menuBar) 

448 

449 def onSelectFont(self, event=None, fsize=None): 

450 if fsize is None: 

451 fsize = self.fontsizes.get(event.GetId(), self.fontsize) 

452 self.fontsize = fsize 

453 

454 def set_fontsize(obj, fsize): 

455 fn = obj.GetFont() 

456 f1, f2 = fn.PixelSize 

457 fn.SetPixelSize(wx.Size(int((f1*fsize/f2)), fsize)) 

458 obj.SetFont(fn) 

459 

460 self.PointSize = fsize 

461 set_fontsize(self, fsize) 

462 set_fontsize(self.mainpanel.output, fsize) 

463 set_fontsize(self.mainpanel.objtree.tree, fsize) 

464 set_fontsize(self.mainpanel.objtree.text, fsize) 

465 self.mainpanel.objtree.text.fontsize = fsize 

466 

467 

468 

469 def onWxInspect(self, event=None): 

470 wx.GetApp().ShowInspectionTool() 

471 

472 def onXRFviewer(self, event=None): 

473 self.larchshell.eval("xrf_plot()") 

474 

475 def onClearInput(self, event=None): 

476 self.larchshell.clear_input() 

477 

478 def onClearInput(self, event=None): 

479 self.larchshell.clear_input() 

480 

481 def show_subframe(self, event=None, name=None, creator=None, **opts): 

482 if name is None or creator is None: 

483 return 

484 shown = False 

485 if name in self.subframes: 

486 try: 

487 self.subframes[name].Raise() 

488 shown = True 

489 except: 

490 del self.subframes[name] 

491 if not shown: 

492 self.subframes[name] = creator(parent=self, 

493 _larch=self.larchshell._larch, 

494 **opts) 

495 self.subframes[name].Show() 

496 

497 def onReadData(self, event=None): 

498 wildcard = 'Data file (*.dat)|*.dat|All files (*.*)|*.*' 

499 dlg = wx.FileDialog(self, message='Open Data File', 

500 defaultDir=get_cwd(), 

501 wildcard=FILE_WILDCARDS, 

502 style=wx.FD_OPEN|wx.FD_CHANGE_DIR) 

503 path = None 

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

505 path = os.path.abspath(dlg.GetPath()).replace('\\', '/') 

506 dlg.Destroy() 

507 

508 if path is None: 

509 return 

510 

511 if is_athena_project(path): 

512 self.show_subframe(name='athena_import', filename=path, 

513 creator=AthenaImporter, 

514 read_ok_cb=self.onReadAthenaProject_OK) 

515 else: 

516 filedir, filename = os.path.split(path) 

517 pref = fix_varname((filename + '_'*8)[:8]).replace('.', '_').lower() 

518 

519 count, maxcount = 1, 9999 

520 groupname = "%s%3.3i" % (pref, count) 

521 while hasattr(self.larchshell.symtable, groupname) and count < maxcount: 

522 count += 1 

523 groupname = '%s%3.3i' % (pref, count) 

524 

525 fh = open(path, 'r') 

526 line1 = fh.readline().lower() 

527 fh.close() 

528 reader = read_ascii 

529 if 'epics stepscan file' in line1: 

530 reader = read_gsexdi 

531 elif 'epics scan' in line1: 

532 reader = gsescan_group 

533 elif 'xdi' in line1: 

534 reader = read_xdi 

535 

536 dgroup = reader(str(path), _larch=self.larchshell._larch) 

537 dgroup._path = path 

538 dgroup._filename = filename 

539 dgroup._groupname = groupname 

540 self.show_subframe(name='coledit', event=None, 

541 creator=ColumnDataFileFrame, 

542 filename=path, 

543 last_array_sel=self.last_array_sel, 

544 read_ok_cb=self.onReadScan_Success) 

545 

546 

547 def onReadScan_Success(self, script, path, groupname=None, array_sel=None, 

548 overwrite=False): 

549 """ called when column data has been selected and is ready to be used""" 

550 self.larchshell.eval(script.format(group=groupname, path=path)) 

551 if array_sel is not None: 

552 self.last_array_sel = array_sel 

553 self.larchshell.flush() 

554 

555 def onReadAthenaProject_OK(self, path, namelist): 

556 """read groups from a list of groups from an athena project file""" 

557 read_cmd = "_prj = read_athena('{path:s}', do_fft=False, do_bkg=False)" 

558 self.larchshell.eval(read_cmd.format(path=path)) 

559 dgroup = None 

560 script = "{group:s} = extract_athenagroup(_prj.{prjgroup:s})" 

561 for gname in namelist: 

562 this = getattr(self.larchshell.symtable._prj, gname) 

563 gid = str(getattr(this, 'athena_id', gname)) 

564 self.larchshell.eval(script.format(group=gid, prjgroup=gname)) 

565 self.larchshell.eval("del _prj") 

566 

567 def onRunScript(self, event=None): 

568 wildcard = 'Larch file (*.lar)|*.lar|All files (*.*)|*.*' 

569 dlg = wx.FileDialog(self, message='Open and Run Larch Script', 

570 wildcard=wildcard, 

571 style=wx.FD_OPEN|wx.FD_CHANGE_DIR) 

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

573 fout = os.path.abspath(dlg.GetPath()) 

574 path, fname = os.path.split(fout) 

575 os.chdir(path) 

576 text = "run('%s')" % fname 

577 self.larchshell.write("%s\n" % text) 

578 wx.CallAfter(self.larchshell.eval, text) 

579 dlg.Destroy() 

580 

581 def onSaveHistory(self, event=None): 

582 wildcard = 'Larch file (*.lar)|*.lar|All files (*.*)|*.*' 

583 deffile = 'history.lar' 

584 dlg = wx.FileDialog(self, message='Save Session History File', 

585 wildcard=wildcard, 

586 defaultFile=deffile, 

587 style=wx.FD_SAVE|wx.FD_CHANGE_DIR) 

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

589 fout = os.path.abspath(dlg.GetPath()) 

590 self._larch.input.history.save(fout, session_only=True) 

591 self.SetStatusText("Wrote %s" % fout, 0) 

592 dlg.Destroy() 

593 

594 def onText(self, event=None): 

595 text = event.GetString() 

596 self.larchshell.write("%s\n" % text) 

597 self.input.Clear() 

598 if text.lower() in ('quit', 'exit', 'quit()', 'exit()'): 

599 if self.exit_on_close: 

600 self.onExit() 

601 else: 

602 self.panel.AddToHistory(text) 

603 wx.CallAfter(self.larchshell.eval, text) 

604 

605 def onChangeDir(self, event=None): 

606 dlg = wx.DirDialog(None, 'Choose a Working Directory', 

607 defaultPath = get_cwd(), 

608 style = wx.DD_DEFAULT_STYLE) 

609 

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

611 os.chdir(dlg.GetPath()) 

612 dlg.Destroy() 

613 return get_cwd() 

614 

615 def onAbout(self, event=None): 

616 about_msg = """LarchGui: 

617 %s""" % (make_banner(withlibraries=True)) 

618 

619 dlg = wx.MessageDialog(self, about_msg, 

620 "About LarchGui", wx.OK | wx.ICON_INFORMATION) 

621 dlg.ShowModal() 

622 dlg.Destroy() 

623 

624 def onVersions(self, event=None): 

625 vdat = version_data(with_libraries=True) 

626 out = [] 

627 for key, val in vdat.items(): 

628 out.append(f"{key:20s}: {val}") 

629 version_message = '\n'.join(out) 

630 dlg = wx.Dialog(self, wx.ID_ANY, size=(700, 400), 

631 title='Larch Versions') 

632 

633 font = get_font(self.fontsize) 

634 dlg.SetFont(font) 

635 panel = wx.Panel(dlg) 

636 txt = wx.StaticText(panel, label=version_message) 

637 s = wx.Sizer 

638 sizer = wx.BoxSizer(wx.VERTICAL) 

639 sizer.Add(txt, 1, wx.LEFT|wx.ALL, 5) 

640 panel.SetSizer(sizer) 

641 panel_pack(dlg, panel) 

642 dlg.Show() 

643 

644 def onShow(self, event=None): 

645 if event.Show: 

646 self.mainpanel.update() 

647 

648 def onClose(self, event=None): 

649 try: 

650 self.Hide() 

651 except: 

652 pass 

653 

654 def onExit(self, event=None, force=False, with_sysexit=True): 

655 if not self.exit_on_close: 

656 self.Hide() 

657 return 

658 if force: 

659 ret = wx.ID_YES 

660 else: 

661 dlg = wx.MessageDialog(None, 'Really Quit?', 'Question', 

662 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) 

663 ret = dlg.ShowModal() 

664 

665 if ret == wx.ID_YES: 

666 try: 

667 self._larch.input.history.save() 

668 except: 

669 pass 

670 try: 

671 try: 

672 for a in self.GetChildren(): 

673 a.Destroy() 

674 except: 

675 pass 

676 self.Destroy() 

677 

678 except: 

679 pass 

680 if with_sysexit: 

681 sys.exit() 

682 else: 

683 try: 

684 event.Veto() 

685 except: 

686 pass 

687 

688class LarchApp(LarchWxApp): 

689 "simple app to wrap LarchFrame" 

690 def __init__(self, with_inspection=False, **kws): 

691 self.with_inspection = with_inspection 

692 LarchWxApp.__init__(self, **kws) 

693 

694 def createApp(self): 

695 frame = LarchFrame(exit_on_close=True, with_inspection=self.with_inspection) 

696 frame.Show() 

697 self.SetTopWindow(frame) 

698 return True 

699 

700if __name__ == '__main__': 

701 LarchApp().MainLoop()