class XASFrame(wx.Frame): _about = """Larch XAS GUI: XAS Visualization and Analysis Matt Newville <newville @ cars.uchicago.edu> """ def __init__(self, parent=None, _larch=None, **kws): wx.Frame.__init__(self, parent, -1, size=XASVIEW_SIZE, style=FRAMESTYLE) self.last_array_sel = {} self.paths2read = [] title = "Larch XAS GUI: XAS Visualization and Analysis" self.larch_buffer = parent if not isinstance(parent, LarchFrame): self.larch_buffer = LarchFrame(_larch=_larch) self.larch_buffer.Show() self.larch_buffer.Raise() self.larch = self.larch_buffer.larchshell self.larch.symtable._sys.xas_viewer = Group() self.controller = XASController(wxparent=self, _larch=self.larch) self.current_filename = None self.subframes = {} self.plotframe = None self.SetTitle(title) self.SetSize(XASVIEW_SIZE) self.SetFont(Font(FONTSIZE)) self.larch_buffer.Hide() self.createMainPanel() self.createMenus() self.statusbar = self.CreateStatusBar(2, 0) self.statusbar.SetStatusWidths([-3, -1]) statusbar_fields = ["Initializing....", " "] for i in range(len(statusbar_fields)): self.statusbar.SetStatusText(statusbar_fields[i], i) def createMainPanel(self): display0 = wx.Display(0) client_area = display0.ClientArea xmin, ymin, xmax, ymax = client_area xpos = int((xmax - xmin) * 0.02) + xmin ypos = int((ymax - ymin) * 0.04) + ymin self.SetPosition((xpos, ypos)) splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(250) leftpanel = wx.Panel(splitter) ltop = wx.Panel(leftpanel) def Btn(msg, x, act): b = Button(ltop, msg, size=(x, 30), action=act) b.SetFont(Font(FONTSIZE)) return b sel_none = Btn('Select None', 120, self.onSelNone) sel_all = Btn('Select All', 120, self.onSelAll) self.controller.filelist = FileCheckList( leftpanel, # main=self, select_action=self.ShowFile, remove_action=self.RemoveFile) tsizer = wx.BoxSizer(wx.HORIZONTAL) tsizer.Add(sel_all, 1, LCEN | wx.GROW, 1) tsizer.Add(sel_none, 1, LCEN | wx.GROW, 1) pack(ltop, tsizer) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(ltop, 0, LCEN | wx.GROW, 1) sizer.Add(self.controller.filelist, 1, LCEN | wx.GROW | wx.ALL, 1) pack(leftpanel, sizer) # right hand side panel = wx.Panel(splitter) sizer = wx.BoxSizer(wx.VERTICAL) self.title = SimpleText(panel, 'initializing...', size=(300, -1)) self.title.SetFont(Font(FONTSIZE + 1)) ir = 0 sizer.Add(self.title, 0, LCEN | wx.GROW | wx.ALL, 1) self.nb = flat_nb.FlatNotebook(panel, -1, agwStyle=FNB_STYLE) self.nb.SetTabAreaColour(wx.Colour(250, 250, 250)) self.nb.SetActiveTabColour(wx.Colour(254, 254, 195)) self.nb.SetNonActiveTabTextColour(wx.Colour(10, 10, 128)) self.nb.SetActiveTabTextColour(wx.Colour(128, 0, 0)) self.nb_panels = [] for name, creator in NB_PANELS: _panel = creator(parent=self, controller=self.controller) self.nb.AddPage(_panel, " %s " % name, True) self.nb_panels.append(_panel) sizer.Add(self.nb, 1, LCEN | wx.EXPAND, 2) self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onNBChanged) self.nb.SetSelection(0) pack(panel, sizer) splitter.SplitVertically(leftpanel, panel, 1) wx.CallAfter(self.init_larch) def onNBChanged(self, event=None): idx = self.nb.GetSelection() pan = self.nb_panels[idx] callback = getattr(pan, 'onPanelExposed', None) if callable(callback): callback() def onSelAll(self, event=None): self.controller.filelist.SetCheckedStrings( self.controller.file_groups.keys()) def onSelNone(self, event=None): self.controller.filelist.SetCheckedStrings([]) def init_larch(self): self.SetStatusText('initializing Larch') self.title.SetLabel('') self.controller.init_larch() plotframe = self.controller.get_display(stacked=False) xpos, ypos = self.GetPosition() xsiz, ysiz = self.GetSize() plotframe.SetPosition((xpos + xsiz + 5, ypos)) plotframe.SetSize((ysiz, ysiz)) self.SetStatusText('ready') self.Raise() def write_message(self, s, panel=0): """write a message to the Status Bar""" self.SetStatusText(s, panel) def RemoveFile(self, fname=None, **kws): if fname is not None: s = str(fname) if s in self.controller.file_groups: group = self.controller.file_groups.pop(s) def ShowFile(self, evt=None, groupname=None, process=True, plot=True, **kws): filename = None if evt is not None: filename = str(evt.GetString()) if groupname is None and filename is not None: groupname = self.controller.file_groups[filename] if not hasattr(self.larch.symtable, groupname): return dgroup = self.controller.get_group(groupname) if filename is None: filename = dgroup.filename self.title.SetLabel(filename) self.current_filename = filename self.controller.group = dgroup self.controller.groupname = groupname cur_panel = self.nb_panels[self.nb.GetSelection()] if process: cur_panel.fill_form(dgroup=dgroup) cur_panel.process(dgroup=dgroup) if plot and hasattr(cur_panel, 'plot'): cur_panel.plot(dgroup=dgroup) def createMenus(self): # ppnl = self.plotpanel self.menubar = wx.MenuBar() # fmenu = wx.Menu() group_menu = wx.Menu() data_menu = wx.Menu() ppeak_menu = wx.Menu() m = {} MenuItem(self, fmenu, "&Open Data File\tCtrl+O", "Open Data File", self.onReadDialog) MenuItem(self, fmenu, "&Save Project\tCtrl+S", "Save Session to Project File", self.onSaveProject) MenuItem(self, fmenu, "Export Selected Groups to Athena Project", "Export Selected Groups to Athena Project", self.onExportAthena) MenuItem(self, fmenu, "Export Selected Groups to CSV", "Export Selected Groups to CSV", self.onExportCSV) fmenu.AppendSeparator() MenuItem(self, fmenu, 'Show Larch Buffer\tCtrl+L', 'Show Larch Programming Buffer', self.onShowLarchBuffer) MenuItem(self, fmenu, 'Save Larch Script of History\tCtrl+H', 'Save Session History as Larch Script', self.onSaveLarchHistory) if WX_DEBUG: MenuItem(self, fmenu, "&Inspect \tCtrl+J", " wx inspection tool ", self.showInspectionTool) MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) MenuItem(self, group_menu, "Copy This Group", "Copy This Group", self.onCopyGroup) MenuItem(self, group_menu, "Rename This Group", "Rename This Group", self.onRenameGroup) MenuItem(self, group_menu, "Remove Selected Groups", "Remove Selected Group", self.onRemoveGroups) MenuItem(self, group_menu, "Merge Selected Groups", "Merge Selected Groups", self.onMergeData) MenuItem(self, data_menu, "Deglitch Data", "Deglitch Data", self.onDeglitchData) MenuItem(self, data_menu, "Recalibrate Energy", "Recalibrate Energy", self.onEnergyCalibrateData) MenuItem(self, data_menu, "Smooth Data", "Smooth Data", self.onSmoothData) MenuItem(self, data_menu, "Rebin Data", "Rebin Data", self.onRebinData) MenuItem(self, data_menu, "Deconvolve Data", "Deconvolution of Data", self.onDeconvolveData) MenuItem(self, data_menu, "Correct Over-absorption", "Correct Over-absorption", self.onCorrectOverAbsorptionData) MenuItem(self, ppeak_menu, "&Read Fit Model\tCtrl+R", "Read Fit Model from File", self.onLoadFitResult) fsave = MenuItem(self, ppeak_menu, "Save Fit Model", "Save Fit Model to File", self.onSaveFitResult) fexport = MenuItem(self, ppeak_menu, "Export Data and Fit", "Export Data and Fit", self.onExportFitResult) self.afterfit_menus = (fsave, fexport) for m in self.afterfit_menus: m.Enable(False) self.menubar.Append(fmenu, "&File") self.menubar.Append(group_menu, "Groups") self.menubar.Append(data_menu, "Data") self.menubar.Append(ppeak_menu, "PreEdgePeaks") self.SetMenuBar(self.menubar) self.Bind(wx.EVT_CLOSE, self.onClose) def onShowLarchBuffer(self, evt=None): if self.larch_buffer is None: self.larch_buffer = LarchFrame(_larch=self.larch) self.larch_buffer.Show() self.larch_buffer.Raise() def onSaveLarchHistory(self, evt=None): wildcard = 'Larch file (*.lar)|*.lar|All files (*.*)|*.*' path = FileSave(self, message='Save Session History as Larch Script', wildcard=wildcard, default_file='xas_viewer_history.lar') if path is not None: self.larch._larch.input.history.save(path, session_only=True) self.SetStatusText("Wrote %s" % path, 0) def onExportCSV(self, evt=None): group_ids = self.controller.filelist.GetCheckedStrings() savegroups = [] groupnames = [] for checked in group_ids: groupname = self.controller.file_groups[str(checked)] dgroup = self.controller.get_group(groupname) savegroups.append(dgroup) groupnames.append(groupname) if len(savegroups) < 1: Popup(self, "No files selected to export to CSV", "No files selected") return deffile = "%s_%i.csv" % (groupname, len(groupnames)) wcards = 'CSV Files (*.csv)|*.cvs|All files (*.*)|*.*' outfile = FileSave(self, 'Export Selected Groups to CSV File', default_file=deffile, wildcard=wcards) if outfile is None: return groups2csv(savegroups, outfile, x='energy', y='norm', _larch=self.larch) def onExportAthena(self, evt=None): groups = [] for checked in self.controller.filelist.GetCheckedStrings(): groups.append(self.controller.file_groups[str(checked)]) if len(groups) < 1: Popup(self, "No files selected to export to Athena", "No files selected") return self.save_athena_project(groups[0], groups, prompt=True) def onSaveProject(self, evt=None): groups = [gname for gname in self.controller.file_groups] if len(groups) < 1: Popup(self, "No files selected to export to Athena", "No files selected") return self.save_athena_project(groups[0], groups, prompt=True) def save_athena_project(self, filename, grouplist, prompt=True): if len(grouplist) < 1: return savegroups = [self.controller.get_group(gname) for gname in grouplist] deffile = "%s_%i.prj" % (filename, len(grouplist)) wcards = 'Athena Projects (*.prj)|*.prj|All files (*.*)|*.*' outfile = FileSave(self, 'Save Groups to Athena Project File', default_file=deffile, wildcard=wcards) if outfile is None: return aprj = AthenaProject(filename=outfile, _larch=self.larch) for label, grp in zip(grouplist, savegroups): aprj.add_group(grp, label=label) aprj.save(use_gzip=True) def onConfigDataProcessing(self, event=None): pass def onNewGroup(self, datagroup): """ install and display a new group, as from 'copy / modify' Note: this is a group object, not the groupname or filename """ dgroup = datagroup self.install_group(dgroup.groupname, dgroup.filename, overwrite=False) self.ShowFile(groupname=dgroup.groupname) def onCopyGroup(self, event=None): fname = self.current_filename if fname is None: fname = self.controller.filelist.GetStringSelection() ngroup = self.controller.copy_group(fname) self.onNewGroup(ngroup) def onRenameGroup(self, event=None): fname = self.current_filename = self.controller.filelist.GetStringSelection( ) if fname is None: return dlg = RenameDialog(self, fname) res = dlg.GetResponse() dlg.Destroy() if res.ok: selected = [] for checked in self.controller.filelist.GetCheckedStrings(): selected.append(str(checked)) if self.current_filename in selected: selected.remove(self.current_filename) selected.append(res.newname) groupname = self.controller.file_groups.pop(fname) self.controller.file_groups[res.newname] = groupname self.controller.filelist.rename_item(self.current_filename, res.newname) dgroup = self.controller.get_group(groupname) dgroup.filename = self.current_filename = res.newname self.controller.filelist.SetCheckedStrings(selected) self.controller.filelist.SetStringSelection(res.newname) def onRemoveGroups(self, event=None): groups = [] for checked in self.controller.filelist.GetCheckedStrings(): groups.append(str(checked)) if len(groups) < 1: return dlg = RemoveDialog(self, groups) res = dlg.GetResponse() dlg.Destroy() if res.ok: filelist = self.controller.filelist all_fnames = filelist.GetItems() for fname in groups: gname = self.controller.file_groups.pop(fname) delattr(self.controller.symtable, gname) all_fnames.remove(fname) filelist.Clear() for name in all_fnames: filelist.Append(name) def onMergeData(self, event=None): groups = [] for checked in self.controller.filelist.GetCheckedStrings(): groups.append(self.controller.file_groups[str(checked)]) if len(groups) < 1: return outgroup = unique_name('merge', self.controller.file_groups) dlg = MergeDialog(self, groups, outgroup=outgroup) res = dlg.GetResponse() dlg.Destroy() if res.ok: fname = res.group gname = fix_varname(res.group.lower()) yname = 'norm' if res.ynorm else 'mu' self.controller.merge_groups(groups, master=res.master, yarray=yname, outgroup=gname) self.install_group(gname, fname, overwrite=False) self.controller.filelist.SetStringSelection(fname) def onDeglitchData(self, event=None): DeglitchDialog(self, self.controller).Show() def onSmoothData(self, event=None): SmoothDataDialog(self, self.controller).Show() def onRebinData(self, event=None): RebinDataDialog(self, self.controller).Show() def onCorrectOverAbsorptionData(self, event=None): OverAbsorptionDialog(self, self.controller).Show() def onEnergyCalibrateData(self, event=None): EnergyCalibrateDialog(self, self.controller).Show() def onDeconvolveData(self, event=None): DeconvolutionDialog(self, self.controller).Show() def onConfigDataFitting(self, event=None): pass def showInspectionTool(self, event=None): app = wx.GetApp() app.ShowInspectionTool() def onAbout(self, evt): dlg = wx.MessageDialog(self, self._about, "About Larch XAS GUI", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def onClose(self, event): dlg = QuitDialog(self) res = dlg.GetResponse() dlg.Destroy() if not res.ok: return if res.save: groups = [gname for gname in self.controller.file_groups] if len(groups) > 0: self.save_athena_project(groups[0], groups, prompt=True) self.controller.save_config() self.controller.get_display().Destroy() if self.larch_buffer is not None: try: self.larch_buffer.Destroy() except: pass time.sleep(0.05) for nam in dir(self.larch.symtable._plotter): obj = getattr(self.larch.symtable._plotter, nam) time.sleep(0.05) try: obj.Destroy() except: pass for name, wid in self.subframes.items(): if wid is not None: try: wid.Destroy() except: pass for nam in dir(self.larch.symtable._sys.wx): obj = getattr(self.larch.symtable._sys.wx, nam) self.Destroy() def show_subframe(self, name, frameclass, **opts): shown = False if name in self.subframes: try: self.subframes[name].Raise() shown = True except: del self.subframes[name] if not shown: self.subframes[name] = frameclass(self, **opts) def onSelectColumns(self, event=None): dgroup = self.controller.get_group() self.show_subframe('readfile', ColumnDataFileFrame, group=dgroup.raw, last_array_sel=self.last_array_sel, _larch=self.larch, read_ok_cb=partial(self.onRead_OK, overwrite=True)) def onLoadFitResult(self, event=None): self.nb.SetSelection(1) self.nb_panels[1].onLoadFitResult(event=event) def onSaveFitResult(self, event=None): self.nb_panels[1].onSaveFitResult(event=event) def onExportFitResult(self, event=None): self.nb_panels[1].onExportFitResult(event=event) def onReadDialog(self, event=None): dlg = wx.FileDialog(self, message="Read Data File", defaultDir=os.getcwd(), wildcard=FILE_WILDCARDS, style=wx.FD_OPEN | wx.FD_MULTIPLE) self.paths2read = [] if dlg.ShowModal() == wx.ID_OK: self.paths2read = dlg.GetPaths() dlg.Destroy() if len(self.paths2read) < 1: return path = self.paths2read.pop(0) path = path.replace('\\', '/') do_read = True if path in self.controller.file_groups: do_read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path, 'Re-read file?')) if do_read: self.onRead(path) def onRead(self, path): filedir, filename = os.path.split(path) if self.controller.get_config('chdir_on_fileopen'): os.chdir(filedir) self.controller.set_workdir() # check for athena projects if is_athena_project(path): kwargs = dict(filename=path, _larch=self.controller.larch, read_ok_cb=self.onReadAthenaProject_OK) self.show_subframe('athena_import', AthenaImporter, **kwargs) else: kwargs = dict(filename=path, _larch=self.larch_buffer.larchshell, last_array_sel=self.last_array_sel, read_ok_cb=self.onRead_OK) self.show_subframe('readfile', ColumnDataFileFrame, **kwargs) def onReadAthenaProject_OK(self, path, namelist): """read groups from a list of groups from an athena project file""" self.larch.eval( "_prj = read_athena('{path:s}', do_fft=False, do_bkg=False)". format(path=path)) dgroup = None script = "{group:s} = extract_athenagroup(_prj.{prjgroup:s})" for gname in namelist: this = getattr(self.larch.symtable._prj, gname) gid = str(getattr(this, 'athena_id', gname)) self.larch.eval(script.format(group=gid, prjgroup=gname)) dgroup = self.install_group(gid, gname, process=True, plot=False) self.larch.eval("del _prj") def onRead_OK(self, script, path, groupname=None, array_sel=None, overwrite=False): """ called when column data has been selected and is ready to be used overwrite: whether to overwrite the current datagroup, as when editing a datagroup """ abort_read = False filedir, filename = os.path.split(path) if not overwrite and hasattr(self.larch.symtable, groupname): newname = file2groupname(filename, symtable=self.larch.symtable) msg = """Warning: groupname '%s' already used. Will use groupname '%s' instead """ % (groupname, newname) dlg = wx.MessageDialog(self, msg, 'Warning', wx.OK | wx.CANCEL) abort_read = (wx.ID_OK != dlg.ShowModal()) dlg.Destroy() groupname = newname if abort_read: return self.larch.eval(script.format(group=groupname, path=path)) if array_sel is not None: self.last_array_sel = array_sel self.install_group(groupname, filename, overwrite=overwrite) for path in self.paths2read: path = path.replace('\\', '/') filedir, filename = os.path.split(path) gname = file2groupname(filename, symtable=self.larch.symtable) self.larch.eval(script.format(group=gname, path=path)) self.install_group(gname, filename, overwrite=True) def install_group(self, groupname, filename, overwrite=False, process=True, plot=True): """add groupname / filename to list of available data groups""" thisgroup = getattr(self.larch.symtable, groupname) thisgroup.groupname = groupname thisgroup.filename = filename datatype = getattr(thisgroup, 'datatype', 'raw') # file /group may already exist in list if filename in self.controller.file_groups and not overwrite: for i in range(1, 101): ftest = "%s (%i)" % (filename, i) if ftest not in self.controller.file_groups: filename = ftest break if filename not in self.controller.file_groups: self.controller.filelist.Append(filename) self.controller.file_groups[filename] = groupname self.nb.SetSelection(0) self.ShowFile(groupname=groupname, process=process, plot=plot) self.controller.filelist.SetStringSelection(filename) return thisgroup
class XASFrame(wx.Frame): _about = """Larch XAS GUI: XAS Visualization and Analysis Matt Newville <newville @ cars.uchicago.edu> """ def __init__(self, parent=None, _larch=None, filename=None, **kws): wx.Frame.__init__(self, parent, -1, size=XASVIEW_SIZE, style=FRAMESTYLE) self.last_array_sel = {} self.paths2read = [] title = "Larch XAS GUI: XAS Visualization and Analysis" self.larch_buffer = parent if not isinstance(parent, LarchFrame): self.larch_buffer = LarchFrame(_larch=_larch, is_standalone=False) self.larch_buffer.Show() self.larch_buffer.Raise() self.larch = self.larch_buffer.larchshell self.larch.symtable._sys.xas_viewer = Group() self.controller = XASController(wxparent=self, _larch=self.larch) self.subframes = {} self.plotframe = None self.SetTitle(title) self.SetSize(XASVIEW_SIZE) self.SetFont(Font(FONTSIZE)) self.larch_buffer.Hide() self.createMainPanel() self.createMenus() self.statusbar = self.CreateStatusBar(2, style=wx.STB_DEFAULT_STYLE) self.statusbar.SetStatusWidths([-3, -1]) statusbar_fields = [" ", "initializing...."] for i in range(len(statusbar_fields)): self.statusbar.SetStatusText(statusbar_fields[i], i) self.current_filename = filename self.Show() if filename is not None: wx.CallAfter(self.onRead, filename) def createMainPanel(self): display0 = wx.Display(0) client_area = display0.ClientArea xmin, ymin, xmax, ymax = client_area xpos = int((xmax - xmin) * 0.02) + xmin ypos = int((ymax - ymin) * 0.04) + ymin self.SetPosition((xpos, ypos)) splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(250) leftpanel = wx.Panel(splitter) ltop = wx.Panel(leftpanel) def Btn(msg, x, act): b = Button(ltop, msg, size=(x, 30), action=act) b.SetFont(Font(FONTSIZE)) return b sel_none = Btn('Select None', 120, self.onSelNone) sel_all = Btn('Select All', 120, self.onSelAll) self.controller.filelist = FileCheckList(leftpanel, select_action=self.ShowFile, remove_action=self.RemoveFile) tsizer = wx.BoxSizer(wx.HORIZONTAL) tsizer.Add(sel_all, 1, LEFT | wx.GROW, 1) tsizer.Add(sel_none, 1, LEFT | wx.GROW, 1) pack(ltop, tsizer) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(ltop, 0, LEFT | wx.GROW, 1) sizer.Add(self.controller.filelist, 1, LEFT | wx.GROW | wx.ALL, 1) pack(leftpanel, sizer) # right hand side panel = wx.Panel(splitter) sizer = wx.BoxSizer(wx.VERTICAL) self.title = SimpleText(panel, 'initializing...', size=(300, -1)) self.title.SetFont(Font(FONTSIZE + 2)) ir = 0 sizer.Add(self.title, 0, LEFT | wx.GROW | wx.ALL, 1) self.nb = flatnotebook(panel, NB_PANELS, panelkws=dict(controller=self.controller), on_change=self.onNBChanged) sizer.Add(self.nb, 1, LEFT | wx.EXPAND, 2) pack(panel, sizer) splitter.SplitVertically(leftpanel, panel, 1) wx.CallAfter(self.init_larch) def onNBChanged(self, event=None): callback = getattr(self.nb.GetCurrentPage(), 'onPanelExposed', None) if callable(callback): callback() def onSelAll(self, event=None): self.controller.filelist.select_all() def onSelNone(self, event=None): self.controller.filelist.select_none() def init_larch(self): self.SetStatusText('initializing Larch', 1) self.title.SetLabel('') self.controller.init_larch() plotframe = self.controller.get_display(stacked=False) xpos, ypos = self.GetPosition() xsiz, ysiz = self.GetSize() plotframe.SetPosition((xpos + xsiz + 5, ypos)) plotframe.SetSize((600, 650)) self.SetStatusText('ready', 1) self.Raise() def write_message(self, msg, panel=0): """write a message to the Status Bar""" self.statusbar.SetStatusText(msg, panel) def RemoveFile(self, fname=None, **kws): if fname is not None: s = str(fname) if s in self.controller.file_groups: group = self.controller.file_groups.pop(s) def ShowFile(self, evt=None, groupname=None, process=True, plot=True, **kws): filename = None if evt is not None: filename = str(evt.GetString()) if groupname is None and filename is not None: groupname = self.controller.file_groups[filename] if not hasattr(self.larch.symtable, groupname): return dgroup = self.controller.get_group(groupname) if dgroup is None: return if filename is None: filename = dgroup.filename self.title.SetLabel(filename) self.current_filename = filename self.controller.group = dgroup self.controller.groupname = groupname cur_panel = self.nb.GetCurrentPage() if process: cur_panel.fill_form(dgroup) cur_panel.skip_process = True cur_panel.process(dgroup=dgroup) if plot and hasattr(cur_panel, 'plot'): cur_panel.plot(dgroup=dgroup) cur_panel.skip_process = False def createMenus(self): # ppnl = self.plotpanel self.menubar = wx.MenuBar() fmenu = wx.Menu() group_menu = wx.Menu() data_menu = wx.Menu() # ppeak_menu = wx.Menu() m = {} MenuItem(self, fmenu, "&Open Data File\tCtrl+O", "Open Data File", self.onReadDialog) MenuItem(self, fmenu, "&Save Project\tCtrl+S", "Save Session to Project File", self.onSaveProject) MenuItem(self, fmenu, "Export Selected Groups to Athena Project", "Export Selected Groups to Athena Project", self.onExportAthena) MenuItem(self, fmenu, "Export Selected Groups to CSV", "Export Selected Groups to CSV", self.onExportCSV) fmenu.AppendSeparator() MenuItem(self, fmenu, 'Show Larch Buffer\tCtrl+L', 'Show Larch Programming Buffer', self.onShowLarchBuffer) MenuItem(self, fmenu, 'Save Larch Script of History\tCtrl+H', 'Save Session History as Larch Script', self.onSaveLarchHistory) if WX_DEBUG: MenuItem(self, fmenu, "&Inspect \tCtrl+J", " wx inspection tool ", self.showInspectionTool) MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) MenuItem(self, group_menu, "Copy This Group", "Copy This Group", self.onCopyGroup) MenuItem(self, group_menu, "Rename This Group", "Rename This Group", self.onRenameGroup) MenuItem(self, group_menu, "Remove Selected Groups", "Remove Selected Group", self.onRemoveGroups) group_menu.AppendSeparator() MenuItem(self, group_menu, "Merge Selected Groups", "Merge Selected Groups", self.onMergeData) group_menu.AppendSeparator() MenuItem(self, group_menu, "Freeze Selected Groups", "Freeze Selected Groups", self.onFreezeGroups) MenuItem(self, group_menu, "UnFreeze Selected Groups", "UnFreeze Selected Groups", self.onUnFreezeGroups) MenuItem(self, data_menu, "Deglitch Data", "Deglitch Data", self.onDeglitchData) MenuItem(self, data_menu, "Recalibrate Energy", "Recalibrate Energy", self.onEnergyCalibrateData) MenuItem(self, data_menu, "Smooth Data", "Smooth Data", self.onSmoothData) MenuItem(self, data_menu, "Rebin Data", "Rebin Data", self.onRebinData) MenuItem(self, data_menu, "Deconvolve Data", "Deconvolution of Data", self.onDeconvolveData) MenuItem(self, data_menu, "Correct Over-absorption", "Correct Over-absorption", self.onCorrectOverAbsorptionData) MenuItem(self, data_menu, "Add and Subtract Sepctra", "Calculations of Spectra", self.onSpectraCalc) self.menubar.Append(fmenu, "&File") self.menubar.Append(group_menu, "Groups") self.menubar.Append(data_menu, "Data") # self.menubar.Append(ppeak_menu, "PreEdgePeaks") self.SetMenuBar(self.menubar) self.Bind(wx.EVT_CLOSE, self.onClose) def onShowLarchBuffer(self, evt=None): if self.larch_buffer is None: self.larch_buffer = LarchFrame(_larch=self.larch, is_standalone=False) self.larch_buffer.Show() self.larch_buffer.Raise() def onSaveLarchHistory(self, evt=None): wildcard = 'Larch file (*.lar)|*.lar|All files (*.*)|*.*' path = FileSave(self, message='Save Session History as Larch Script', wildcard=wildcard, default_file='xas_viewer_history.lar') if path is not None: self.larch._larch.input.history.save(path, session_only=True) self.write_message("Wrote history %s" % path, 0) def onExportCSV(self, evt=None): filenames = self.controller.filelist.GetCheckedStrings() if len(filenames) < 1: Popup(self, "No files selected to export to CSV", "No files selected") return dlg = ExportCSVDialog(self, filenames) res = dlg.GetResponse() dlg.Destroy() if res.ok: savegroups = [self.controller.filename2group(res.master)] for fname in filenames: dgroup = self.controller.filename2group(fname) if dgroup not in savegroups: savegroups.append(dgroup) groups2csv(savegroups, res.filename, x='energy', y=res.yarray, _larch=self.larch) self.write_message("Exported CSV file %s" % (res.filename)) def onExportAthena(self, evt=None): groups = [] for checked in self.controller.filelist.GetCheckedStrings(): groups.append(self.controller.file_groups[str(checked)]) if len(groups) < 1: Popup(self, "No files selected to export to Athena", "No files selected") return self.save_athena_project(groups[0], groups, prompt=True) def onSaveProject(self, evt=None): groups = [gname for gname in self.controller.file_groups] if len(groups) < 1: Popup(self, "No files selected to export to Athena", "No files selected") return self.save_athena_project(groups[0], groups, prompt=True) def save_athena_project(self, filename, grouplist, prompt=True): if len(grouplist) < 1: return savegroups = [self.controller.get_group(gname) for gname in grouplist] deffile = "%s_%i.prj" % (filename, len(grouplist)) wcards = 'Athena Projects (*.prj)|*.prj|All files (*.*)|*.*' outfile = FileSave(self, 'Save Groups to Athena Project File', default_file=deffile, wildcard=wcards) if outfile is None: return aprj = AthenaProject(filename=outfile, _larch=self.larch) for label, grp in zip(grouplist, savegroups): aprj.add_group(grp) aprj.save(use_gzip=True) self.write_message("Saved project file %s" % (outfile)) def onConfigDataProcessing(self, event=None): pass def onNewGroup(self, datagroup): """ install and display a new group, as from 'copy / modify' Note: this is a group object, not the groupname or filename """ dgroup = datagroup self.install_group(dgroup.groupname, dgroup.filename, overwrite=False) self.ShowFile(groupname=dgroup.groupname) def onCopyGroup(self, event=None): fname = self.current_filename if fname is None: fname = self.controller.filelist.GetStringSelection() ngroup = self.controller.copy_group(fname) self.onNewGroup(ngroup) def onRenameGroup(self, event=None): fname = self.current_filename = self.controller.filelist.GetStringSelection( ) if fname is None: return dlg = RenameDialog(self, fname) res = dlg.GetResponse() dlg.Destroy() if res.ok: selected = [] for checked in self.controller.filelist.GetCheckedStrings(): selected.append(str(checked)) if self.current_filename in selected: selected.remove(self.current_filename) selected.append(res.newname) groupname = self.controller.file_groups.pop(fname) self.controller.file_groups[res.newname] = groupname self.controller.filelist.rename_item(self.current_filename, res.newname) dgroup = self.controller.get_group(groupname) dgroup.filename = self.current_filename = res.newname self.controller.filelist.SetCheckedStrings(selected) self.controller.filelist.SetStringSelection(res.newname) def onRemoveGroups(self, event=None): groups = [] for checked in self.controller.filelist.GetCheckedStrings(): groups.append(str(checked)) if len(groups) < 1: return dlg = RemoveDialog(self, groups) res = dlg.GetResponse() dlg.Destroy() if res.ok: filelist = self.controller.filelist all_fnames = filelist.GetItems() for fname in groups: gname = self.controller.file_groups.pop(fname) delattr(self.controller.symtable, gname) all_fnames.remove(fname) filelist.Clear() for name in all_fnames: filelist.Append(name) def onFreezeGroups(self, event=None): self._freeze_handler(True) def onUnFreezeGroups(self, event=None): self._freeze_handler(False) def _freeze_handler(self, freeze): current_filename = self.current_filename reproc_group = None for fname in self.controller.filelist.GetCheckedStrings(): groupname = self.controller.file_groups[str(fname)] dgroup = self.controller.get_group(groupname) if fname == current_filename: reproc_group = groupname dgroup.is_frozen = freeze if reproc_group is not None: self.ShowFile(groupname=reproc_group, process=True) def onMergeData(self, event=None): groups = OrderedDict() for checked in self.controller.filelist.GetCheckedStrings(): cname = str(checked) groups[cname] = self.controller.file_groups[cname] if len(groups) < 1: return outgroup = common_startstring(list(groups.keys())) outgroup = "%s (merge %d)" % (outgroup, len(groups)) outgroup = unique_name(outgroup, self.controller.file_groups) dlg = MergeDialog(self, list(groups.keys()), outgroup=outgroup) res = dlg.GetResponse() dlg.Destroy() if res.ok: fname = res.group gname = fix_varname(res.group.lower()) master = self.controller.file_groups[res.master] yname = 'norm' if res.ynorm else 'mu' self.controller.merge_groups(list(groups.values()), master=master, yarray=yname, outgroup=gname) self.install_group(gname, fname, overwrite=False) self.controller.filelist.SetStringSelection(fname) def onDeglitchData(self, event=None): DeglitchDialog(self, self.controller).Show() def onSmoothData(self, event=None): SmoothDataDialog(self, self.controller).Show() def onRebinData(self, event=None): RebinDataDialog(self, self.controller).Show() def onCorrectOverAbsorptionData(self, event=None): OverAbsorptionDialog(self, self.controller).Show() def onSpectraCalc(self, event=None): SpectraCalcDialog(self, self.controller).Show() def onEnergyCalibrateData(self, event=None): EnergyCalibrateDialog(self, self.controller).Show() def onDeconvolveData(self, event=None): DeconvolutionDialog(self, self.controller).Show() def onConfigDataFitting(self, event=None): pass def showInspectionTool(self, event=None): app = wx.GetApp() app.ShowInspectionTool() def onAbout(self, evt): dlg = wx.MessageDialog(self, self._about, "About Larch XAS GUI", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def onClose(self, event): dlg = QuitDialog(self) dlg.Raise() dlg.SetWindowStyle(wx.STAY_ON_TOP) res = dlg.GetResponse() dlg.Destroy() if not res.ok: return if res.save: groups = [gname for gname in self.controller.file_groups] if len(groups) > 0: self.save_athena_project(groups[0], groups, prompt=True) self.controller.save_config() wx.CallAfter(self.controller.close_all_displays) if self.larch_buffer is not None: wx.CallAfter(self.larch_buffer.Destroy) for name, wid in self.subframes.items(): if hasattr(wid, 'Destroy'): wx.CallAfter(wid.Destroy) self.Destroy() def show_subframe(self, name, frameclass, **opts): shown = False if name in self.subframes: try: self.subframes[name].Raise() shown = True except: del self.subframes[name] if not shown: self.subframes[name] = frameclass(self, **opts) def onSelectColumns(self, event=None): dgroup = self.controller.get_group() self.show_subframe('readfile', ColumnDataFileFrame, group=dgroup.raw, last_array_sel=self.last_array_sel, _larch=self.larch, read_ok_cb=partial(self.onRead_OK, overwrite=True)) def onLoadFitResult(self, event=None): pass # print("onLoadFitResult??") # self.nb.SetSelection(1) # self.nb_panels[1].onLoadFitResult(event=event) def onReadDialog(self, event=None): dlg = wx.FileDialog(self, message="Read Data File", defaultDir=os.getcwd(), wildcard=FILE_WILDCARDS, style=wx.FD_OPEN | wx.FD_MULTIPLE) self.paths2read = [] if dlg.ShowModal() == wx.ID_OK: self.paths2read = dlg.GetPaths() dlg.Destroy() if len(self.paths2read) < 1: return path = self.paths2read.pop(0) path = path.replace('\\', '/') do_read = True if path in self.controller.file_groups: do_read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path, 'Re-read file?')) if do_read: self.onRead(path) def onRead(self, path): filedir, filename = os.path.split(os.path.abspath(path)) if self.controller.get_config('chdir_on_fileopen'): os.chdir(filedir) self.controller.set_workdir() # check for athena projects if is_athena_project(path): kwargs = dict(filename=path, _larch=self.controller.larch, read_ok_cb=self.onReadAthenaProject_OK) self.show_subframe('athena_import', AthenaImporter, **kwargs) else: kwargs = dict(filename=path, _larch=self.larch_buffer.larchshell, last_array_sel=self.last_array_sel, read_ok_cb=self.onRead_OK) self.show_subframe('readfile', ColumnDataFileFrame, **kwargs) def onReadAthenaProject_OK(self, path, namelist): """read groups from a list of groups from an athena project file""" self.larch.eval( "_prj = read_athena('{path:s}', do_fft=False, do_bkg=False)". format(path=path)) dgroup = None script = "{group:s} = extract_athenagroup(_prj.{prjgroup:s})" cur_panel = self.nb.GetCurrentPage() cur_panel.skip_plotting = True for gname in namelist: cur_panel.skip_plotting = (gname == namelist[-1]) this = getattr(self.larch.symtable._prj, gname) gid = str(getattr(this, 'athena_id', gname)) if self.larch.symtable.has_group(gid): count, prefix = 0, gname[:3] while count < 1e7 and self.larch.symtable.has_group(gid): gid = prefix + make_hashkey(length=7) count += 1 self.larch.eval(script.format(group=gid, prjgroup=gname)) dgroup = self.install_group(gid, gname, process=True, plot=False) self.larch.eval("del _prj") cur_panel.skip_plotting = False if len(namelist) > 0: gname = self.controller.file_groups[namelist[0]] self.ShowFile(groupname=gname, process=True, plot=True) self.write_message("read %d datasets from %s" % (len(namelist), path)) def onRead_OK(self, script, path, groupname=None, filename=None, array_sel=None, overwrite=False): """ called when column data has been selected and is ready to be used overwrite: whether to overwrite the current datagroup, as when editing a datagroup """ if groupname is None: return abort_read = False filedir, real_filename = os.path.split(path) if filename is None: filename = real_filename if not overwrite and hasattr(self.larch.symtable, groupname): groupname = file2groupname(real_filename, symtable=self.larch.symtable) if abort_read: return self.larch.eval(script.format(group=groupname, path=path)) if array_sel is not None: self.last_array_sel = array_sel self.install_group(groupname, filename, overwrite=overwrite) # check if rebin is needed thisgroup = getattr(self.larch.symtable, groupname) do_rebin = False if thisgroup.datatype == 'xas': try: en = thisgroup.energy except: do_rebin = True en = thisgroup.energy = thisgroup.xdat # test for rebinning: # too many data points # unsorted energy data or data in angle # too fine a step size at the end of the data range if (len(en) > 1200 or any(np.diff(en) < 0) or ((max(en) - min(en)) > 300 and (np.diff(en[-50:]).mean() < 0.75))): msg = """This dataset may need to be rebinned. Rebin now?""" dlg = wx.MessageDialog(self, msg, 'Warning', wx.YES | wx.NO) do_rebin = (wx.ID_YES == dlg.ShowModal()) dlg.Destroy() for path in self.paths2read: path = path.replace('\\', '/') filedir, real_filename = os.path.split(path) gname = file2groupname(real_filename, symtable=self.larch.symtable) self.larch.eval(script.format(group=gname, path=path)) self.install_group(gname, real_filename, overwrite=overwrite) self.write_message("read %s" % (real_filename)) if do_rebin: RebinDataDialog(self, self.controller).Show() def install_group(self, groupname, filename, overwrite=False, process=True, rebin=False, plot=True): """add groupname / filename to list of available data groups""" thisgroup = getattr(self.larch.symtable, groupname) datatype = getattr(thisgroup, 'datatype', 'raw') # file /group may already exist in list if filename in self.controller.file_groups and not overwrite: fbase, i = filename, 0 while i < 10000 and filename in self.controller.file_groups: filename = "%s_%d" % (fbase, i) i += 1 cmds = [ "%s.groupname = '%s'" % (groupname, groupname), "%s.filename = '%s'" % (groupname, filename) ] self.larch.eval('\n'.join(cmds)) self.controller.filelist.Append(filename) self.controller.file_groups[filename] = groupname self.nb.SetSelection(0) self.ShowFile(groupname=groupname, process=process, plot=plot) self.controller.filelist.SetStringSelection(filename) return thisgroup