Esempio n. 1
0
class XYFitPanel(wx.Panel):
    def __init__(self, parent=None, controller=None, **kws):

        wx.Panel.__init__(self, parent, -1, size=(550, 500), **kws)
        self.parent = parent
        self.controller = controller
        self.larch = controller.larch
        self.fit_components = OrderedDict()
        self.fit_model = None
        self.fit_params = None
        self.user_added_params = None
        self.summary = None
        self.sizer = wx.GridBagSizer(10, 6)
        self.build_display()
        self.pick2_timer = wx.Timer(self)
        self.pick2_group = None
        self.Bind(wx.EVT_TIMER, self.onPick2Timer, self.pick2_timer)
        self.pick2_t0 = 0.
        self.pick2_timeout = 15.

        self.pick2erase_timer = wx.Timer(self)
        self.pick2erase_panel = None
        self.Bind(wx.EVT_TIMER, self.onPick2EraseTimer, self.pick2erase_timer)

    def build_display(self):

        self.mod_nb = flat_nb.FlatNotebook(self, -1, agwStyle=FNB_STYLE)
        self.mod_nb.SetTabAreaColour(wx.Colour(250, 250, 250))
        self.mod_nb.SetActiveTabColour(wx.Colour(254, 254, 195))

        self.mod_nb.SetNonActiveTabTextColour(wx.Colour(10, 10, 128))
        self.mod_nb.SetActiveTabTextColour(wx.Colour(128, 0, 0))
        self.mod_nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onNBChanged)

        self.param_panel = AllParamsPanel(self, controller=self.controller)
        # self.mod_nb.AddPage(self.param_panel, 'Parameters', True)

        range_row = wx.Panel(self)
        rsizer = wx.BoxSizer(wx.HORIZONTAL)

        xmin_sel = BitmapButton(range_row,
                                get_icon('plus'),
                                action=partial(self.on_selpoint, opt='xmin'),
                                tooltip='use last point selected from plot')
        xmax_sel = BitmapButton(range_row,
                                get_icon('plus'),
                                action=partial(self.on_selpoint, opt='xmax'),
                                tooltip='use last point selected from plot')

        opts = {'size': (70, -1), 'gformat': True}
        self.xmin = FloatCtrl(range_row, value=-np.inf, **opts)
        self.xmax = FloatCtrl(range_row, value=np.inf, **opts)

        rsizer.Add(SimpleText(range_row, 'Fit Range X=[ '), 0, LCEN, 3)
        rsizer.Add(xmin_sel, 0, LCEN, 3)
        rsizer.Add(self.xmin, 0, LCEN, 3)
        rsizer.Add(SimpleText(range_row, ' : '), 0, LCEN, 3)
        rsizer.Add(xmax_sel, 0, LCEN, 3)
        rsizer.Add(self.xmax, 0, LCEN, 3)
        rsizer.Add(SimpleText(range_row, ' ]  '), 0, LCEN, 3)
        rsizer.Add(
            Button(range_row,
                   'Full Data Range',
                   size=(150, -1),
                   action=self.onResetRange), 0, LCEN, 3)

        pack(range_row, rsizer)

        action_row = wx.Panel(self)
        rsizer = wx.BoxSizer(wx.HORIZONTAL)

        self.plot_comps = Check(action_row,
                                label='Plot Components?',
                                default=True,
                                size=(150, -1))

        rsizer.Add(
            Button(action_row, 'Run Fit', size=(100, -1),
                   action=self.onRunFit), 0, RCEN, 3)
        savebtn = Button(action_row,
                         'Save Fit',
                         size=(100, -1),
                         action=self.onSaveFit)
        savebtn.Disable()
        rsizer.Add(savebtn, 0, LCEN, 3)

        rsizer.Add(
            Button(action_row,
                   'Plot Current Model',
                   size=(150, -1),
                   action=self.onShowModel), 0, LCEN, 3)
        rsizer.Add(self.plot_comps, 0, LCEN, 3)

        pack(action_row, rsizer)

        models_row = wx.Panel(self)
        rsizer = wx.BoxSizer(wx.HORIZONTAL)

        self.model_choice = Choice(models_row,
                                   size=(200, -1),
                                   choices=ModelChoices['peaks'],
                                   action=self.addModel)

        rsizer.Add(SimpleText(models_row, ' Add Model Type: '), 0, LCEN, 3)

        rsizer.Add(
            Choice(models_row,
                   size=(100, -1),
                   choices=ModelTypes,
                   action=self.onModelTypes), 0, LCEN, 3)

        rsizer.Add(SimpleText(models_row, ' Model: '), 0, LCEN, 3)

        rsizer.Add(self.model_choice, 0, LCEN, 3)

        pack(models_row, rsizer)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany([(range_row, 0, LCEN, 4), ((9, 9), 0, LCEN, 4),
                       (models_row, 0, LCEN, 4), ((9, 9), 0, LCEN, 4),
                       (action_row, 0, LCEN, 4), ((9, 9), 0, LCEN, 4),
                       (HLine(self, size=(550, 3)), 0, LCEN, 4),
                       ((10, 10), 0, LCEN, 2),
                       (self.mod_nb, 1, LCEN | wx.GROW, 10)])

        pack(self, sizer)

    def onNBChanged(self, event=None):
        idx = self.mod_nb.GetSelection()
        if self.mod_nb.GetPage(idx) is self.param_panel:
            self.build_fitmodel()
            self.param_panel.show_parameters(self.fit_params,
                                             self.user_added_params)

    def onModelTypes(self, event=None):
        modtype = event.GetString().lower()
        self.model_choice.SetChoices(ModelChoices[modtype])

    def addModel(self, event=None, model=None):
        if model is None and event is not None:
            model = event.GetString()
        if model is None or model.startswith('<'):
            return

        curmodels = [
            "c%i_" % (i + 1) for i in range(1 + len(self.fit_components))
        ]
        for comp in self.fit_components:
            if comp in curmodels:
                curmodels.remove(comp)

        prefix = curmodels[0]

        label = "%s(prefix='%s')" % (model, prefix)
        title = "%s: %s" % (prefix[:-1], (model + ' ' * 8)[:8])
        mclass_kws = {'prefix': prefix}
        if 'step' in model.lower():
            form = model.lower().replace('step', '').strip()

            if form.startswith('err'): form = 'erf'
            label = "Step(form='%s', prefix='%s')" % (form, prefix)
            title = "%s: Step %s" % (prefix[:-1], form[:3])
            mclass = lm_models.StepModel
            mclass_kws['form'] = form
            minst = mclass(form=form, prefix=prefix)
        else:
            mclass = getattr(lm_models, model + 'Model')
            minst = mclass(prefix=prefix)

        panel = GridPanel(self.mod_nb, ncols=1, nrows=1, pad=1, itemstyle=CEN)

        def SLabel(label, size=(80, -1), **kws):
            return SimpleText(panel,
                              label,
                              size=size,
                              style=wx.ALIGN_LEFT,
                              **kws)

        usebox = Check(panel, default=True, label='Use?', size=(75, -1))
        delbtn = Button(panel,
                        'Delete Model',
                        size=(120, -1),
                        action=partial(self.onDeleteComponent, prefix=prefix))
        pick2msg = SimpleText(panel, "    ", size=(75, -1))
        pick2btn = Button(panel,
                          'Pick Data Range',
                          size=(135, -1),
                          action=partial(self.onPick2Points, prefix=prefix))

        # SetTip(mname,  'Label for the model component')
        SetTip(usebox, 'Use this component in fit?')
        SetTip(delbtn, 'Delete this model component')
        SetTip(pick2btn, 'Select X range on Plot to Guess Initial Values')

        panel.Add(HLine(panel, size=(520, 3)), style=wx.ALIGN_CENTER, dcol=6)

        panel.Add(SLabel(label, size=(200, -1), colour='#0000AA'),
                  dcol=3,
                  newrow=True)
        panel.AddMany((usebox, pick2msg, pick2btn))

        panel.Add(SLabel("Parameter"), newrow=True)
        panel.AddMany((SLabel("Value"), SLabel("Type"), SLabel("Min"),
                       SLabel("Max"), SLabel("Expression")))

        parwids = OrderedDict()

        parnames = sorted(minst.param_names)

        for a in minst._func_allargs:
            pname = "%s%s" % (prefix, a)
            if (pname not in parnames and a in minst.param_hints
                    and a not in minst.independent_vars):
                parnames.append(pname)

        for pname in parnames:
            sname = pname[len(prefix):]
            hints = minst.param_hints.get(sname, {})

            par = Parameter(name=pname, value=0, vary=True)
            if 'min' in hints:
                par.min = hints['min']
            if 'max' in hints:
                par.max = hints['max']
            if 'value' in hints:
                par.value = hints['value']
            if 'expr' in hints:
                par.expr = hints['expr']

            pwids = ParameterWidgets(panel,
                                     par,
                                     name_size=80,
                                     expr_size=175,
                                     float_size=80,
                                     prefix=prefix,
                                     widgets=('name', 'value', 'minval',
                                              'maxval', 'vary', 'expr'))
            parwids[par.name] = pwids
            panel.Add(pwids.name, newrow=True)
            panel.AddMany((pwids.value, pwids.vary, pwids.minval, pwids.maxval,
                           pwids.expr))

        for sname, hint in minst.param_hints.items():
            pname = "%s%s" % (prefix, sname)
            if 'expr' in hint and pname not in parnames:
                par = Parameter(name=pname, value=0, expr=hint['expr'])

                pwids = ParameterWidgets(panel,
                                         par,
                                         name_size=80,
                                         expr_size=275,
                                         float_size=80,
                                         prefix=prefix,
                                         widgets=('name', 'value', 'vary',
                                                  'expr'))
                parwids[par.name] = pwids
                panel.Add(pwids.name, newrow=True)
                panel.AddMany((pwids.value, pwids.vary))
                panel.Add(pwids.expr, dcol=3, style=wx.ALIGN_RIGHT)
                pwids.value.Disable()
                pwids.vary.Disable()

        panel.Add(HLine(panel, size=(90, 3)),
                  style=wx.ALIGN_CENTER,
                  newrow=True)
        panel.Add(delbtn, dcol=2)
        panel.Add(HLine(panel, size=(250, 3)), dcol=3, style=wx.ALIGN_CENTER)

        fgroup = Group(prefix=prefix,
                       title=title,
                       mclass=mclass,
                       mclass_kws=mclass_kws,
                       usebox=usebox,
                       panel=panel,
                       parwids=parwids,
                       float_size=65,
                       expr_size=150,
                       pick2_msg=pick2msg)

        self.fit_components[prefix] = fgroup
        panel.pack()

        self.mod_nb.AddPage(panel, title, True)
        sx, sy = self.GetSize()
        self.SetSize((sx, sy + 1))
        self.SetSize((sx, sy))

    def onDeleteComponent(self, evt=None, prefix=None):
        fgroup = self.fit_components.get(prefix, None)
        if fgroup is None:
            return

        for i in range(self.mod_nb.GetPageCount()):
            if fgroup.title == self.mod_nb.GetPageText(i):
                self.mod_nb.DeletePage(i)

        for attr in dir(fgroup):
            setattr(fgroup, attr, None)

        self.fit_components.pop(prefix)

        sx, sy = self.GetSize()
        self.SetSize((sx, sy + 1))
        self.SetSize((sx, sy))

    def onPick2EraseTimer(self, evt=None):
        """erases line trace showing automated 'Pick 2' guess """
        self.pick2erase_timer.Stop()
        panel = self.pick2erase_panel
        ntrace = panel.conf.ntrace - 1
        trace = panel.conf.get_mpl_line(ntrace)
        panel.conf.get_mpl_line(ntrace).set_data(np.array([]), np.array([]))
        panel.conf.ntrace = ntrace
        panel.draw()

    def onPick2Timer(self, evt=None):
        """checks for 'Pick 2' events, and initiates 'Pick 2' guess
        for a model from the selected data range
        """
        try:
            plotframe = self.controller.get_display(stacked=False)
            curhist = plotframe.cursor_hist[:]
            plotframe.Raise()
        except:
            return

        if (time.time() - self.pick2_t0) > self.pick2_timeout:
            msg = self.pick2_group.pick2_msg.SetLabel(" ")
            plotframe.cursor_hist = []
            self.pick2_timer.Stop()
            return

        if len(curhist) < 2:
            self.pick2_group.pick2_msg.SetLabel("%i/2" % (len(curhist)))
            return

        self.pick2_group.pick2_msg.SetLabel("done.")
        self.pick2_timer.Stop()

        # guess param values
        xcur = (curhist[0][0], curhist[1][0])
        xmin, xmax = min(xcur), max(xcur)

        dgroup = getattr(self.larch.symtable, self.controller.groupname)
        x, y = dgroup.x, dgroup.y
        i0 = index_of(dgroup.x, xmin)
        i1 = index_of(dgroup.x, xmax)
        x, y = dgroup.x[i0:i1 + 1], dgroup.y[i0:i1 + 1]

        mod = self.pick2_group.mclass(prefix=self.pick2_group.prefix)
        parwids = self.pick2_group.parwids
        try:
            guesses = mod.guess(y, x=x)
        except:
            return

        for name, param in guesses.items():
            if name in parwids:
                parwids[name].value.SetValue(param.value)

        dgroup._tmp = mod.eval(guesses, x=dgroup.x)
        plotframe = self.controller.get_display(stacked=False)
        plotframe.cursor_hist = []
        plotframe.oplot(dgroup.x, dgroup._tmp)
        self.pick2erase_panel = plotframe.panel

        self.pick2erase_timer.Start(5000)

    def onPick2Points(self, evt=None, prefix=None):
        fgroup = self.fit_components.get(prefix, None)
        if fgroup is None:
            return

        plotframe = self.controller.get_display(stacked=False)
        plotframe.Raise()

        plotframe.cursor_hist = []
        fgroup.npts = 0
        self.pick2_group = fgroup

        if fgroup.pick2_msg is not None:
            fgroup.pick2_msg.SetLabel("0/2")

        self.pick2_t0 = time.time()
        self.pick2_timer.Start(250)

    def onSaveFit(self, event=None):
        dgroup = self.get_datagroup()
        deffile = dgroup.filename.replace('.', '_') + '.fitconf'
        outfile = FileSave(self,
                           'Save Fit Configuration and Results',
                           default_file=deffile,
                           wildcard=FITCONF_WILDCARDS)

        if outfile is None:
            return

        buff = ['#XYFit Config version 1']
        buff.append(
            json.dumps(encode4js(self.summary), encoding='UTF-8', default=str))
        buff.append('')
        try:
            fout = open(outfile, 'w')
            fout.write('\n'.join(buff))
            fout.close()
        except IOError:
            print('could not write %s' % outfile)

    def onResetRange(self, event=None):
        dgroup = self.get_datagroup()
        self.xmin.SetValue(min(dgroup.x))
        self.xmax.SetValue(max(dgroup.x))

    def on_selpoint(self, evt=None, opt='xmin'):
        xval = None
        try:
            xval = self.larch.symtable._plotter.plot1_x
        except:
            xval = None
        if xval is not None:
            if opt == 'xmin':
                self.xmin.SetValue(xval)
            elif opt == 'xmax':
                self.xmax.SetValue(xval)

    def get_datagroup(self):
        dgroup = None
        if self.controller.groupname is not None:
            try:
                dgroup = getattr(self.larch.symtable,
                                 self.controller.groupname)
            except:
                pass
        return dgroup

    def get_xranges(self, x):
        xmin, xmax = min(x), max(x)
        i1, i2 = 0, len(x)
        _xmin = self.xmin.GetValue()
        _xmax = self.xmax.GetValue()
        if _xmin > min(x):
            i1 = index_of(x, _xmin)
            xmin = x[i1]
        if _xmax < max(x):
            i2 = index_of(x, _xmax) + 1
            xmax = x[i2]
        xv1 = max(min(x), xmin - (xmax - xmin) / 5.0)
        xv2 = min(max(x), xmax + (xmax - xmin) / 5.0)
        return i1, i2, xv1, xv2

    def build_fitmodel(self):
        """ use fit components to build model"""
        dgroup = self.get_datagroup()
        fullmodel = None
        params = Parameters()
        self.summary = {'components': [], 'options': {}}
        for comp in self.fit_components.values():
            if comp.usebox is not None and comp.usebox.IsChecked():
                for parwids in comp.parwids.values():
                    params.add(parwids.param)
                self.summary['components'].append(
                    (comp.mclass.__name__, comp.mclass_kws))
                thismodel = comp.mclass(**comp.mclass_kws)
                if fullmodel is None:
                    fullmodel = thismodel
                else:
                    fullmodel += thismodel

        self.fit_model = fullmodel
        self.fit_params = params

        if dgroup is not None:
            i1, i2, xv1, xv2 = self.get_xranges(dgroup.x)
            xsel = dgroup.x[slice(i1, i2)]
            dgroup.xfit = xsel
            dgroup.yfit = self.fit_model.eval(self.fit_params, x=xsel)
            dgroup.ycomps = self.fit_model.eval_components(
                params=self.fit_params, x=xsel)
        return dgroup

    def onShowModel(self, event=None):
        dgroup = self.build_fitmodel()
        if dgroup is not None:
            with_components = (self.plot_comps.IsChecked()
                               and len(dgroup.ycomps) > 1)

            self.plot_fitmodel(dgroup,
                               show_resid=False,
                               with_components=with_components)

    def plot_fitmodel(self, dgroup, show_resid=False, with_components=None):
        if dgroup is None:
            return
        i1, i2, xv1, xv2 = self.get_xranges(dgroup.x)
        ysel = dgroup.y[slice(i1, i2)]

        plotframe = self.controller.get_display(stacked=True)
        plotframe.plot(dgroup.xfit,
                       ysel,
                       new=True,
                       panel='top',
                       xmin=xv1,
                       xmax=xv2,
                       label='data',
                       xlabel=dgroup.plot_xlabel,
                       ylabel=dgroup.plot_ylabel,
                       title='Larch XYFit: %s' % dgroup.filename)

        plotframe.oplot(dgroup.xfit, dgroup.yfit, label='fit')

        plotframe.plot(dgroup.xfit,
                       ysel - dgroup.yfit,
                       grid=False,
                       marker='o',
                       markersize=4,
                       linewidth=1,
                       panel='bot')

        if with_components is None:
            with_components = (self.plot_comps.IsChecked()
                               and len(dgroup.ycomps) > 1)
        if with_components:
            for label, _y in dgroup.ycomps.items():
                plotframe.oplot(dgroup.xfit,
                                _y,
                                label=label,
                                style='short dashed')

        line_opts = dict(color='#AAAAAA',
                         label='_nolegend_',
                         linewidth=1,
                         zorder=-5)
        plotframe.panel_bot.axes.axhline(0, **line_opts)
        axvline = plotframe.panel.axes.axvline
        if i1 > 0:
            axvline(dgroup.x[i1], **line_opts)

        if i2 < len(dgroup.x):
            axvline(dgroup.x[i2 - 1], **line_opts)

        plotframe.panel.canvas.draw()

    def onRunFit(self, event=None):
        dgroup = self.build_fitmodel()
        if dgroup is None:
            return
        i1, i2, xv1, xv2 = self.get_xranges(dgroup.x)
        dgroup.xfit = dgroup.x[slice(i1, i2)]
        ysel = dgroup.y[slice(i1, i2)]
        weights = np.ones(len(ysel))

        if hasattr(dgroup, 'yerr'):
            yerr = dgroup.yerr
            if not isinstance(yerr, np.ndarray):
                yerr = yerr * np.ones(len(ysel))
            else:
                yerr = yerr[slice(i1, i2)]
            yerr_min = 1.e-9 * ysel.mean()
            yerr[np.where(yerr < yerr_min)] = yerr_min
            weights = 1.0 / yerr

        result = self.fit_model.fit(ysel,
                                    params=self.fit_params,
                                    x=dgroup.xfit,
                                    weights=weights,
                                    method='leastsq')
        self.summary['xmin'] = xv1
        self.summary['xmax'] = xv2
        for attr in ('aic', 'bic', 'chisqr', 'redchi', 'ci_out', 'covar',
                     'flatchain', 'success', 'nan_policy', 'nfev', 'ndata',
                     'nfree', 'nvarys', 'init_values'):
            self.summary[attr] = getattr(result, attr)
        self.summary['params'] = result.params

        dgroup.fit_history = []
        dgroup.fit_history.append(self.summary)

        dgroup.yfit = result.best_fit
        dgroup.ycomps = self.fit_model.eval_components(params=result.params,
                                                       x=dgroup.xfit)

        with_components = (self.plot_comps.IsChecked()
                           and len(dgroup.ycomps) > 1)

        self.plot_fitmodel(dgroup,
                           show_resid=True,
                           with_components=with_components)

        # print(" == fit model == ", self.fit_model)
        # print(" == fit result == ", result)

        model_repr = self.fit_model._reprstring(long=True)
        report = fit_report(result,
                            show_correl=True,
                            min_correl=0.25,
                            sort_pars=True)

        report = '[[Model]]\n    %s\n%s\n' % (model_repr, report)
        self.summary['report'] = report

        self.controller.show_report(report)

        # fill parameters with best fit values
        allparwids = {}
        for comp in self.fit_components.values():
            if comp.usebox is not None and comp.usebox.IsChecked():
                for name, parwids in comp.parwids.items():
                    allparwids[name] = parwids

        for pname, par in result.params.items():
            if pname in allparwids:
                allparwids[pname].value.SetValue(par.value)
Esempio n. 2
0
class ScanViewerFrame(wx.Frame):
    _about = """Scan 2D Plotter
  Matt Newville <newville @ cars.uchicago.edu>
  """

    def __init__(self, _larch=None, **kws):

        wx.Frame.__init__(self, None, -1, style=FRAMESTYLE)
        self.file_groups = {}
        self.file_paths = []
        title = "Column Data File Viewer"
        self.larch = _larch
        self.larch_buffer = None
        self.subframes = {}
        self.plotframe = None
        self.groupname = None
        self.SetTitle(title)
        self.SetSize((850, 650))
        self.SetFont(Font(10))

        self.config = {'chdir_on_fileopen': True}

        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)
        read_workdir('scanviewer.dat')

    def createMainPanel(self):
        splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
        splitter.SetMinimumPaneSize(225)

        self.filelist = wx.ListBox(splitter)
        self.filelist.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.filelist.Bind(wx.EVT_LISTBOX, self.ShowFile)

        self.detailspanel = self.createDetailsPanel(splitter)

        splitter.SplitVertically(self.filelist, self.detailspanel, 1)
        wx.CallAfter(self.init_larch)

    def createDetailsPanel(self, parent):
        mainpanel = wx.Panel(parent)
        mainsizer = wx.BoxSizer(wx.VERTICAL)
        panel = wx.Panel(mainpanel)
        sizer = wx.GridBagSizer(8, 7)

        self.title = SimpleText(panel, 'initializing...')
        ir = 0
        sizer.Add(self.title, (ir, 0), (1, 6), LCEN, 2)
        # x-axis

        self.xarr = Choice(panel,
                           choices=[],
                           action=self.onColumnChoices,
                           size=(120, -1))
        self.xop = Choice(panel,
                          choices=('', 'log'),
                          action=self.onColumnChoices,
                          size=(90, -1))

        ir += 1
        sizer.Add(SimpleText(panel, 'X = '), (ir, 0), (1, 1), CEN, 0)
        sizer.Add(self.xop, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(SimpleText(panel, '('), (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.xarr, (ir, 3), (1, 1), RCEN, 0)
        sizer.Add(SimpleText(panel, ')'), (ir, 4), (1, 1), CEN, 0)

        self.yops = []
        self.yarr = []

        opts = {
            'choices': [],
            'size': (120, -1),
            'action': self.onColumnChoices
        }
        for i in range(3):
            self.yarr.append(Choice(panel, **opts))

        for opts, sel, siz in ((PRE_OPS, 0, 90), (ARR_OPS, 3, 50), (ARR_OPS, 3,
                                                                    50)):
            w1 = Choice(panel,
                        choices=opts,
                        action=self.onColumnChoices,
                        size=(siz, -1))
            w1.SetSelection(sel)
            self.yops.append(w1)

        ir += 1
        label = 'Y = '
        sizer.Add(SimpleText(panel, label), (ir, 0), (1, 1), CEN, 0)
        sizer.Add(self.yops[0], (ir, 1), (1, 1), CEN, 0)
        sizer.Add(SimpleText(panel, '[('), (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.yarr[0], (ir, 3), (1, 1), CEN, 0)
        sizer.Add(self.yops[1], (ir, 4), (1, 1), CEN, 0)
        sizer.Add(self.yarr[1], (ir, 5), (1, 1), CEN, 0)
        sizer.Add(SimpleText(panel, ')'), (ir, 6), (1, 1), LCEN, 0)
        ir += 1
        sizer.Add(self.yops[2], (ir, 4), (1, 1), CEN, 0)
        sizer.Add(self.yarr[2], (ir, 5), (1, 1), CEN, 0)
        sizer.Add(SimpleText(panel, ']'), (ir, 6), (1, 1), LCEN, 0)

        self.use_deriv = Check(panel,
                               default=False,
                               label='Use Derivative?',
                               action=self.onColumnChoices)
        self.dtcorr = Check(panel,
                            default=True,
                            label='correct deadtime?',
                            action=self.onColumnChoices)
        ir += 1
        sizer.Add(self.use_deriv, (ir, 0), (1, 3), LCEN, 0)
        sizer.Add(self.dtcorr, (ir, 3), (1, 3), LCEN, 0)

        pack(panel, sizer)

        self.nb = flat_nb.FlatNotebook(mainpanel, -1, agwStyle=FNB_STYLE)

        self.nb.SetTabAreaColour(wx.Colour(248, 248, 240))
        self.nb.SetActiveTabColour(wx.Colour(254, 254, 195))

        self.nb.SetNonActiveTabTextColour(wx.Colour(40, 40, 180))
        self.nb.SetActiveTabTextColour(wx.Colour(80, 0, 0))

        self.xas_panel = self.CreateXASPanel(self.nb)  # mainpanel)
        self.fit_panel = self.CreateFitPanel(self.nb)  # mainpanel)

        self.nb.AddPage(self.fit_panel, ' General Analysis ', True)
        self.nb.AddPage(self.xas_panel, ' XAS Processing ', True)
        mainsizer.Add(panel, 0, LCEN | wx.EXPAND, 2)

        btnbox = wx.Panel(mainpanel)
        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
        for ttl, opt in (('New Plot', 'new'), ('Over Plot (left)', 'left'),
                         ('Over Plot (right)', 'right')):

            btnsizer.Add(
                Button(btnbox,
                       ttl,
                       size=(135, -1),
                       action=partial(self.onPlot, opt=opt)), LCEN, 1)

        pack(btnbox, btnsizer)
        mainsizer.Add(btnbox, 0, LCEN, 2)
        mainsizer.Add(self.nb, 1, LCEN | wx.EXPAND, 2)

        pack(mainpanel, mainsizer)

        return mainpanel

    def CreateFitPanel(self, parent):
        panel = wx.Panel(parent)
        tpan = wx.Panel(panel)
        self.fit_model = Choice(tpan,
                                size=(100, -1),
                                choices=('Gaussian', 'Lorentzian', 'Voigt',
                                         'Linear', 'Quadratic', 'Step',
                                         'Rectangle', 'Exponential'))
        self.fit_bkg = Choice(tpan,
                              size=(100, -1),
                              choices=('None', 'constant', 'linear',
                                       'quadratic'))
        self.fit_step = Choice(tpan,
                               size=(100, -1),
                               choices=('linear', 'error function', 'arctan'))

        tsizer = wx.GridBagSizer(10, 4)
        tsizer.Add(SimpleText(tpan, 'Fit Model: '), (0, 0), (1, 1), LCEN)
        tsizer.Add(self.fit_model, (0, 1), (1, 1), LCEN)

        tsizer.Add(SimpleText(tpan, 'Background: '), (0, 2), (1, 1), LCEN)
        tsizer.Add(self.fit_bkg, (0, 3), (1, 1), LCEN)

        tsizer.Add(
            Button(tpan, 'Show Fit', size=(100, -1), action=self.onFitPeak),
            (1, 1), (1, 1), LCEN)

        tsizer.Add(SimpleText(tpan, 'Step Form: '), (1, 2), (1, 1), LCEN)
        tsizer.Add(self.fit_step, (1, 3), (1, 1), LCEN)

        pack(tpan, tsizer)

        self.fit_report = RichTextCtrl(panel,
                                       size=(525, 250),
                                       style=wx.VSCROLL | wx.NO_BORDER)

        self.fit_report.SetEditable(False)
        self.fit_report.SetFont(Font(9))

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(tpan, 0, wx.GROW | wx.ALL, 2)
        sizer.Add(self.fit_report, 1, LCEN | wx.GROW, 2)
        pack(panel, sizer)
        return panel

    def InitializeXASPanel(self):
        if self.groupname is None:
            lgroup = None
        lgroup = getattr(self.larch.symtable, self.groupname)
        self.xas_e0.SetValue(getattr(lgroup, 'e0', 0))
        self.xas_step.SetValue(getattr(lgroup, 'edge_step', 0))
        self.xas_pre1.SetValue(getattr(lgroup, 'pre1', -200))
        self.xas_pre2.SetValue(getattr(lgroup, 'pre2', -30))
        self.xas_nor1.SetValue(getattr(lgroup, 'norm1', 50))
        self.xas_nor2.SetValue(getattr(lgroup, 'norm2', -10))

        self.xas_vict.SetSelection(getattr(lgroup, 'nvict', 1))
        self.xas_nnor.SetSelection(getattr(lgroup, 'nnorm', 2))

    def CreateXASPanel(self, parent):
        p = panel = wx.Panel(parent)
        self.xas_autoe0 = Check(panel, default=True, label='auto?')
        self.xas_showe0 = Check(panel, default=True, label='show?')
        self.xas_autostep = Check(panel, default=True, label='auto?')
        self.xas_op = Choice(
            panel,
            size=(225, -1),
            choices=('Raw Data', 'Normalized', 'Derivative',
                     'Normalized + Derivative', 'Pre-edge subtracted',
                     'Raw Data With Pre-edge/Post-edge Curves'),
            action=self.onXASChoice)
        opts = {
            'size': (95, -1),
            'precision': 3
        }  # , 'action': self.onXASChoice}
        self.xas_e0 = FloatCtrl(panel, value=0, **opts)
        self.xas_step = FloatCtrl(panel, value=0, **opts)
        opts['precision'] = 1
        self.xas_pre1 = FloatCtrl(panel, value=-200, **opts)
        self.xas_pre2 = FloatCtrl(panel, value=-30, **opts)
        self.xas_nor1 = FloatCtrl(panel, value=50, **opts)
        self.xas_nor2 = FloatCtrl(panel, value=-50, **opts)
        opts = {
            'size': (50, -1),
            'choices': ('0', '1', '2', '3'),
            'action': self.onXASChoice
        }
        self.xas_vict = Choice(panel, **opts)
        self.xas_nnor = Choice(panel, **opts)
        self.xas_vict.SetSelection(1)
        self.xas_nnor.SetSelection(2)
        sizer = wx.GridBagSizer(10, 4)

        sizer.Add(SimpleText(p, 'Plot XAS as: '), (0, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'E0 : '), (1, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Edge Step: '), (2, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Pre-edge range: '), (3, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Normalization range: '), (4, 0), (1, 1), LCEN)

        sizer.Add(self.xas_op, (0, 1), (1, 3), LCEN)
        sizer.Add(self.xas_e0, (1, 1), (1, 1), LCEN)
        sizer.Add(self.xas_step, (2, 1), (1, 1), LCEN)
        sizer.Add(self.xas_pre1, (3, 1), (1, 1), LCEN)
        sizer.Add(SimpleText(p, ':'), (3, 2), (1, 1), LCEN)
        sizer.Add(self.xas_pre2, (3, 3), (1, 1), LCEN)
        sizer.Add(self.xas_nor1, (4, 1), (1, 1), LCEN)
        sizer.Add(SimpleText(p, ':'), (4, 2), (1, 1), LCEN)
        sizer.Add(self.xas_nor2, (4, 3), (1, 1), LCEN)

        sizer.Add(self.xas_autoe0, (1, 2), (1, 2), LCEN)
        sizer.Add(self.xas_showe0, (1, 4), (1, 2), LCEN)
        sizer.Add(self.xas_autostep, (2, 2), (1, 2), LCEN)

        sizer.Add(SimpleText(p, 'Victoreen:'), (3, 4), (1, 1), LCEN)
        sizer.Add(self.xas_vict, (3, 5), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'PolyOrder:'), (4, 4), (1, 1), LCEN)
        sizer.Add(self.xas_nnor, (4, 5), (1, 1), LCEN)

        pack(panel, sizer)
        return panel

    def onFitPeak(self, evt=None):
        gname = self.groupname

        dtext = []
        model = self.fit_model.GetStringSelection().lower()
        dtext.append('Fit Model: %s' % model)
        bkg = self.fit_bkg.GetStringSelection()
        if bkg == 'None':
            bkg = None
        if bkg is None:
            dtext.append('No Background')
        else:
            dtext.append('Background: %s' % bkg)

        step = self.fit_step.GetStringSelection().lower()
        if model in ('step', 'rectangle'):
            dtext.append('Step form: %s' % step)

        try:
            lgroup = getattr(self.larch.symtable, gname)
            x = lgroup._xdat_
            y = lgroup._ydat_
        except AttributeError:
            self.write_message('need data to fit!')
            return
        if step.startswith('error'):
            step = 'erf'
        elif step.startswith('arctan'):
            step = 'atan'

        pgroup = fit_peak(x,
                          y,
                          model,
                          background=bkg,
                          step=step,
                          _larch=self.larch)

        dtext = '\n'.join(dtext)
        dtext = '%s\n%s\n' % (
            dtext, fit_report(
                pgroup.params, min_correl=0.25, _larch=self.larch))

        self.fit_report.SetEditable(True)
        self.fit_report.SetValue(dtext)
        self.fit_report.SetEditable(False)

        popts1 = dict(style='solid', linewidth=3, marker='None', markersize=4)
        popts2 = dict(style='short dashed',
                      linewidth=2,
                      marker='None',
                      markersize=4)

        lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel)]
        if bkg is None:
            lgroup._fit = pgroup.fit[:]
            lgroup.plot_yarrays.append((lgroup._fit, popts2, 'fit'))
        else:
            lgroup._fit = pgroup.fit[:]
            lgroup._fit_bgr = pgroup.bkg[:]
            lgroup.plot_yarrays.append((lgroup._fit, popts2, 'fit'))
            lgroup.plot_yarrays.append((lgroup._fit_bgr, popts2, 'background'))
        self.onPlot()

    def xas_process(self, gname, new_mu=False, **kws):
        """ process (pre-edge/normalize) XAS data from XAS form, overwriting
        larch group '_y1_' attribute to be plotted
        """

        out = self.xas_op.GetStringSelection().lower()  # raw, pre, norm, flat
        preopts = {'group': gname, 'e0': None}

        lgroup = getattr(self.larch.symtable, gname)
        dtcorr = self.dtcorr.IsChecked()
        if new_mu:
            try:
                del lgroup.e0, lgroup.edge_step
            except:
                pass

        if not self.xas_autoe0.IsChecked():
            e0 = self.xas_e0.GetValue()
            if e0 < max(lgroup._xdat_) and e0 > min(lgroup._xdat_):
                preopts['e0'] = e0

        if not self.xas_autostep.IsChecked():
            preopts['step'] = self.xas_step.GetValue()

        dt = debugtime()

        preopts['pre1'] = self.xas_pre1.GetValue()
        preopts['pre2'] = self.xas_pre2.GetValue()
        preopts['norm1'] = self.xas_nor1.GetValue()
        preopts['norm2'] = self.xas_nor2.GetValue()

        preopts['nvict'] = self.xas_vict.GetSelection()
        preopts['nvict'] = self.xas_vict.GetSelection()
        preopts['nnorm'] = self.xas_nnor.GetSelection()

        preopts['make_flat'] = 'False'
        preopts['group'] = gname
        preopts = ", ".join(["%s=%s" % (k, v) for k, v in preopts.items()])

        preedge_cmd = "pre_edge(%s._xdat_, %s._ydat_, %s)"
        self.larch(preedge_cmd % (gname, gname, preopts))
        if self.xas_autoe0.IsChecked():
            self.xas_e0.SetValue(lgroup.e0)
        if self.xas_autostep.IsChecked():
            self.xas_step.SetValue(lgroup.edge_step)

        details_group = lgroup
        try:
            details_group = lgroup.pre_edge_details
        except:
            pass

        self.xas_pre1.SetValue(details_group.pre1)
        self.xas_pre2.SetValue(details_group.pre2)
        self.xas_nor1.SetValue(details_group.norm1)
        self.xas_nor2.SetValue(details_group.norm2)

        popts1 = dict(style='solid', linewidth=3, marker='None', markersize=4)
        popts2 = dict(style='short dashed',
                      linewidth=2,
                      zorder=-5,
                      marker='None',
                      markersize=4)
        poptsd = dict(style='solid',
                      linewidth=2,
                      zorder=-5,
                      side='right',
                      y2label='derivative',
                      marker='None',
                      markersize=4)

        lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel)]
        y4e0 = lgroup._ydat_
        if out.startswith('raw data with'):
            lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel),
                                   (lgroup.pre_edge, popts2, 'pre edge'),
                                   (lgroup.post_edge, popts2, 'post edge')]
        elif out.startswith('pre'):
            self.larch('%s.pre_edge_sub = %s.norm * %s.edge_step' %
                       (gname, gname, gname))
            lgroup.plot_yarrays = [(lgroup.pre_edge_sub, popts1,
                                    'pre edge subtracted XAFS')]
            y4e0 = lgroup.pre_edge_sub
        elif 'norm' in out and 'deriv' in out:
            lgroup.plot_yarrays = [(lgroup.norm, popts1, 'normalized XAFS'),
                                   (lgroup.dmude, poptsd, 'derivative')]
            y4e0 = lgroup.norm

        elif out.startswith('norm'):
            lgroup.plot_yarrays = [(lgroup.norm, popts1, 'normalized XAFS')]
            y4e0 = lgroup.norm
        elif out.startswith('deriv'):
            lgroup.plot_yarrays = [(lgroup.dmude, popts1, 'derivative')]
            y4e0 = lgroup.dmude

        lgroup.plot_ymarkers = []
        if self.xas_showe0.IsChecked():
            ie0 = index_of(lgroup._xdat_, lgroup.e0)
            lgroup.plot_ymarkers = [(lgroup.e0, y4e0[ie0], {'label': 'e0'})]
        return

    def init_larch(self):
        t0 = time.time()
        if self.larch is None:
            self.larch = Interpreter()
        self.larch.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp())
        self.larch.symtable.set_symbol('_sys.wx.parent', self)

        self.SetStatusText('ready')
        self.datagroups = self.larch.symtable
        self.title.SetLabel('')

    def write_message(self, s, panel=0):
        """write a message to the Status Bar"""
        self.SetStatusText(s, panel)

    def get_data(self, group, arrayname, correct=False):
        if hasattr(group, 'get_data'):
            return group.get_data(arrayname, correct=correct)
        return getattr(group, arrayname, None)

    def onXASChoice(self, evt=None, **kws):
        if self.groupname is None:
            return
        self.xas_process(self.groupname, **kws)
        self.onPlot()

    def onColumnChoices(self, evt=None):
        """column selections changed ..
        recalculate _xdat_ and _ydat_
        arrays for this larch group"""
        dtcorr = self.dtcorr.IsChecked()
        use_deriv = self.use_deriv.IsChecked()
        ix = self.xarr.GetSelection()
        x = self.xarr.GetStringSelection()
        xop = self.xop.GetStringSelection()
        op1 = self.yops[0].GetStringSelection()
        op2 = self.yops[1].GetStringSelection()
        op3 = self.yops[2].GetStringSelection()
        y1 = self.yarr[0].GetStringSelection()
        y2 = self.yarr[1].GetStringSelection()
        y3 = self.yarr[2].GetStringSelection()

        array_sel = {
            'xop': xop,
            'xarr': x,
            'op1': op1,
            'op2': op2,
            'op3': op3,
            'y1': y1,
            'y2': y2,
            'y3': y3,
            'dtcorr': dtcorr,
            'use_deriv': use_deriv
        }
        try:
            gname = self.groupname
            lgroup = getattr(self.larch.symtable, gname)
        except:
            gname = SCANGROUP
            lgroup = getattr(self.larch.symtable, gname)

        xlabel = x
        try:
            xunits = lgroup.array_units[ix]
        except:
            xunits = ''
        if xop != '':
            xlabel = "%s(%s)" % (xop, xlabel)
        if xunits != '':
            xlabel = '%s (%s)' % (xlabel, xunits)

        ylabel = y1
        if y2 == '':
            y2, op2 = '1.0', '*'
        else:
            ylabel = "%s%s%s" % (ylabel, op2, y2)
        if y3 == '':
            y3, op3 = '1.0', '*'
        else:
            ylabel = "(%s)%s%s" % (ylabel, op3, y3)

        if op1 != '':
            ylabel = "%s(%s)" % (op1, ylabel)

        if y1 in ('0.0', '1.0'):
            y1 = float(yl1)
        else:
            y1 = self.get_data(lgroup, y1, correct=dtcorr)

        if y2 in ('0.0', '1.0'):
            y2 = float(y2)
            if op2 == '/': y2 = 1.0
        else:
            y2 = self.get_data(lgroup, y2, correct=dtcorr)
        if y3 in ('0.0', '1.0'):
            y3 = float(y3)
            if op3 == '/': y3 = 1.0
        else:
            y3 = self.get_data(lgroup, y3, correct=dtcorr)
        if x not in ('0', '1'):
            x = self.get_data(lgroup, x)
        lgroup._x = x
        lgroup._y1 = y1
        lgroup._y2 = y2
        lgroup._y3 = y3

        self.larch("%s._xdat_ = %s(%s._x)" % (gname, xop, gname))
        try:
            yexpr = "%s._ydat_ = %s((%s._y1 %s %s._y2) %s %s._y3)" % (
                gname, op1, gname, op2, gname, op3, gname)
            self.larch(yexpr)
        except RuntimeWarning:
            self.larch("%s._ydat_ = %s._y1")

        try:
            if use_deriv:
                d_calc = "%s._ydat_ = gradient(%s._ydat_)/gradient(%s._xdat_)"
                self.larch(d_calc % (gname, gname, gname))
        except:
            pass

        try:
            npts = min(len(lgroup._xdat_), len(lgroup._ydat_))
        except AttributeError:
            print('Error calculating arrays (npts not correct)')
            return

        del lgroup._x, lgroup._y1, lgroup._y2, lgroup._y3

        lgroup.array_sel = array_sel
        lgroup.plot_xlabel = xlabel
        lgroup.plot_ylabel = ylabel
        lgroup._xdat_ = np.array(lgroup._xdat_[:npts])
        lgroup._ydat_ = np.array(lgroup._ydat_[:npts])

        if (self.nb.GetCurrentPage() == self.xas_panel):
            self.xas_process(self.groupname, new_mu=True)
        else:
            lgroup.plot_yarrays = [(lgroup._ydat_, {}, None)]

    def onPlot(self, evt=None, opt='new', npts=None, reprocess=False):

        try:
            self.plotframe.Show()
        except:  #  wx.PyDeadObjectError
            self.plotframe = PlotFrame(None, size=(650, 400))
            self.plotframe.Show()
            self.plotpanel = self.plotframe.panel

        if reprocess:
            if (self.nb.GetCurrentPage() == self.xas_panel):
                self.xas_process(self.groupname, new_mu=True)

        side = 'left'
        update = False
        plotcmd = self.plotpanel.plot
        if opt in ('left', 'right'):
            side = opt
            plotcmd = self.plotpanel.oplot
        elif opt == 'update' and npts > 4:
            plotcmd = self.plotpanel.update_line
            update = True
        if 'new' in opt:
            self.plotpanel.clear()
        popts = {'side': side}

        try:
            gname = self.groupname
            lgroup = getattr(self.larch.symtable, gname)
        except:
            gname = SCANGROUP
            lgroup = getattr(self.larch.symtable, gname)
            return

        if not hasattr(lgroup, '_xdat_'):
            self.onColumnChoices()

        lgroup._xdat_ = np.array(lgroup._xdat_[:npts])
        plot_yarrays = [(lgroup._ydat_, {}, None)]
        if hasattr(lgroup, 'plot_yarrays'):
            plot_yarrays = lgroup.plot_yarrays
        #for yarr in plot_yarrays:
        #    yarr = np.array(yarr[:npts])

        path, fname = os.path.split(lgroup.filename)
        popts['label'] = "%s: %s" % (fname, lgroup.plot_ylabel)
        if side == 'right':
            popts['y2label'] = lgroup.plot_ylabel
        else:
            popts['ylabel'] = lgroup.plot_ylabel

        if plotcmd == self.plotpanel.plot:
            popts['title'] = fname

        if update:
            self.plotpanel.set_xlabel(lgroup.plot_xlabel)
            self.plotpanel.set_ylabel(lgroup.plot_ylabel)
            for itrace, yarr, label in enumerate(plot_yarrays):
                plotcmd(itrace,
                        lgroup._xdat_,
                        yarr[0],
                        draw=True,
                        update_limits=((npts < 5) or (npts % 5 == 0)),
                        **yarr[1])
                self.plotpanel.set_xylims(
                    (min(lgroup._xdat_), max(lgroup._xdat_), min(yarr),
                     max(yarr)))

        else:
            for yarr in plot_yarrays:
                popts.update(yarr[1])
                if yarr[2] is not None:
                    popts['label'] = yarr[2]
                plotcmd(lgroup._xdat_, yarr[0], **popts)
                plotcmd = self.plotpanel.oplot
            if hasattr(lgroup, 'plot_ymarkers'):
                for x, y, opts in lgroup.plot_ymarkers:
                    popts = {'marker': 'o', 'markersize': 4}
                    popts.update(opts)
                    self.plotpanel.oplot([x], [y], **popts)
            self.plotpanel.canvas.draw()

    def onShowLarchBuffer(self, evt=None):
        if self.larch_buffer is None:
            self.larch_buffer = larchframe.LarchFrame(_larch=self.larch)

        self.larch_buffer.Show()
        self.larch_buffer.Raise()

    def ShowFile(self, evt=None, groupname=None, **kws):
        if groupname is None and evt is not None:
            fpath = self.file_paths[evt.GetInt()]
            groupname = self.file_groups[fpath]

        if not hasattr(self.datagroups, groupname):
            print('Error reading file ', groupname)
            return

        self.groupname = groupname
        self.lgroup = getattr(self.datagroups, groupname, None)

        if groupname == SCANGROUP:
            self.lgroup.filename = filename
        elif self.lgroup is not None:
            if hasattr(self.lgroup, 'array_labels'):
                array_labels = self.lgroup.array_labels[:]
            elif hasattr(self.lgroup, 'column_labels'):
                array_labels = self.lgroup.column_labels[:]
            else:
                array_labels = []
                for attr in dir(self.lgroup):
                    if isinstance(getattr(self.lgroup, attr), np.ndarray):
                        array_labels.append(attr)
                self.lgroup.array_labels = array_labels
            self.set_array_labels()
            if hasattr(self.lgroup, 'array_sel'):
                sel = self.lgroup.array_sel
                try:
                    self.xarr.SetStringSelection(sel['xarr'])
                    self.xop.SetStringSelection(sel['xop'])
                    self.yops[0].SetStringSelection(sel['op1'])
                    self.yops[1].SetStringSelection(sel['op2'])
                    self.yops[2].SetStringSelection(sel['op3'])
                    self.yarr[0].SetStringSelection(sel['y1'])
                    self.yarr[1].SetStringSelection(sel['y2'])
                    self.yarr[2].SetStringSelection(sel['y3'])
                    self.dtcorr.SetValue({True: 1, False: 0}[sel['dtcorr']])
                    self.use_deriv.SetValue({
                        True: 1,
                        False: 0
                    }[sel['use_deriv']])
                except:
                    pass

    def set_array_labels(self, labels=None):
        """set choices for array dropdowns from array labels"""
        array_labels = self.lgroup.array_labels
        xcols = array_labels[:]
        ycols = array_labels[:]
        y2cols = array_labels[:] + ['1.0', '0.0', '']
        ncols = len(xcols)
        self.title.SetLabel(self.lgroup.filename)

        _xarr = self.xarr.GetStringSelection()
        if len(_xarr) < 1 or _xarr not in xcols:
            _xarr = xcols[0]

        _yarr = [[], [], []]
        for j in range(3):
            _yarr[j] = self.yarr[j].GetStringSelection()
            if _yarr[j] not in ycols:
                _yarr[j] = ''

        self.xarr.SetItems(xcols)
        self.xarr.SetStringSelection(_xarr)
        for j in range(3):
            if j == 0:
                self.yarr[j].SetItems(ycols)
                if _yarr[j] in ycols and len(_yarr[j]) > 0:
                    self.yarr[j].SetStringSelection(_yarr[j])
                elif ycols[0] == _xarr and len(ycols) > 1:
                    self.yarr[j].SetStringSelection(ycols[1])
            else:
                self.yarr[j].SetItems(y2cols)
                self.yarr[j].SetStringSelection(_yarr[j])

        inb = 0
        for colname in xcols:
            if 'energ' in colname.lower():
                inb = 1
        self.nb.SetSelection(inb)
        if inb == 1:
            self.InitializeXASPanel()

    def createMenus(self):
        # ppnl = self.plotpanel
        self.menubar = wx.MenuBar()
        #
        fmenu = wx.Menu()
        MenuItem(self, fmenu, "&Open Data File\tCtrl+O", "Read Scan File",
                 self.onReadScan)

        MenuItem(self, fmenu, "Show Larch Buffer",
                 "Show Larch Programming Buffer", self.onShowLarchBuffer)

        fmenu.AppendSeparator()

        MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose)

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

        omenu = wx.Menu()
        MenuItem(self, omenu, "Edit Column Labels\tCtrl+E",
                 "Edit Column Labels", self.onEditColumnLabels)

        self.menubar.Append(omenu, "Options")

        # fmenu.AppendSeparator()
        # MenuItem(self, fmenu, "&Copy\tCtrl+C",
        #          "Copy Figure to Clipboard", self.onClipboard)
        # MenuItem(self, fmenu, "&Save\tCtrl+S", "Save Figure", self.onSaveFig)
        # MenuItem(self, fmenu, "&Print\tCtrl+P", "Print Figure", self.onPrint)
        # MenuItem(self, fmenu, "Page Setup", "Print Page Setup", self.onPrintSetup)
        # MenuItem(self, fmenu, "Preview", "Print Preview", self.onPrintPreview)
        #

        #MenuItem(self, pmenu, "Unzoom\tCtrl+Z", "Unzoom Plot", self.onUnzoom)
        ##pmenu.AppendSeparator()
        #MenuItem(self, pmenu, "Toggle Legend\tCtrl+L",
        #         "Toggle Legend on Plot", self.onToggleLegend)
        #MenuItem(self, pmenu, "Toggle Grid\tCtrl+G",
        #         "Toggle Grid on Plot", self.onToggleGrid)
        # self.menubar.Append(pmenu, "Plot Options")
        self.SetMenuBar(self.menubar)
        self.Bind(wx.EVT_CLOSE, self.onClose)

    def onAbout(self, evt):
        dlg = wx.MessageDialog(self, self._about, "About Epics StepScan",
                               wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onClose(self, evt):
        save_workdir('scanviewer.dat')

        try:
            self.plotframe.Destroy()
        except:
            pass
        if self.larch_buffer is not None:
            try:
                self.larch_buffer.onClose()
            except:
                pass

        for nam in dir(self.larch.symtable._sys.wx):
            obj = getattr(self.larch.symtable._sys.wx, nam)
            del obj

        self.Destroy()

    def show_subframe(self, name, frameclass):
        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)

    def onEditColumnLabels(self, evt=None):
        self.show_subframe('coledit', EditColumnFrame)

    def onReadScan(self, evt=None):
        dlg = wx.FileDialog(self,
                            message="Load Column Data File",
                            defaultDir=os.getcwd(),
                            wildcard=FILE_WILDCARDS,
                            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            path = path.replace('\\', '/')
            if path in self.file_groups:
                if wx.ID_YES != popup(self, "Re-read file '%s'?" % path,
                                      'Re-read file?'):
                    return

            gname = '_sview0001'
            count, maxcount = 1, 9999
            while hasattr(self.datagroups, gname) and count < maxcount:
                count += 1
                gname = '_sview%4.4i' % count

            if hasattr(self.datagroups, gname):
                gname = randname()

            parent, fname = os.path.split(path)
            if self.config['chdir_on_fileopen']:
                os.chdir(parent)

            fh = open(path, 'r')
            line1 = fh.readline().lower()
            fh.close()
            reader = 'read_ascii'
            if 'epics scan' in line1:
                reader = 'read_gsescan'
            elif 'xdi' in line1:
                reader = 'read_xdi'
                if 'epics stepscan file' in line1:
                    reader = 'read_gsexdi'

            self.larch("%s = %s('%s')" % (gname, reader, path))
            self.larch("%s.path  = '%s'" % (gname, path))
            self.filelist.Append(fname)
            self.file_paths.append(path)
            self.file_groups[path] = gname

            self.ShowFile(groupname=gname)

        dlg.Destroy()
Esempio n. 3
0
class ProcessPanel(wx.Panel):
    def __init__(self, parent, controller=None, reporter=None, **kws):
        wx.Panel.__init__(self, parent, -1, **kws)

        self.controller = controller
        self.reporter = reporter
        self.needs_update = False
        self.proc_timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.onProcessTimer, self.proc_timer)
        self.proc_timer.Start(100)
        self.build_display()

    def edit_config(self, event=None):
        pass

    def fill(self, dgroup):
        opts = self.controller.get_proc_opts(dgroup)

        self.xshift.SetValue(opts['xshift'])
        self.yshift.SetValue(opts['yshift'])
        self.xscale.SetValue(opts['xscale'])
        self.yscale.SetValue(opts['yscale'])

        self.smooth_op.SetStringSelection(opts['smooth_op'])
        self.smooth_conv.SetStringSelection(opts['smooth_conv'])
        self.smooth_c0.SetValue(opts['smooth_c0'])
        self.smooth_c1.SetValue(opts['smooth_c1'])
        self.smooth_sig.SetValue(opts['smooth_sig'])

        if dgroup.datatype == 'xas':
            self.xas_op.SetStringSelection(opts['xas_op'])
            self.xas_e0.SetValue(opts['e0'])
            self.xas_step.SetValue(opts['edge_step'])
            self.xas_pre1.SetValue(opts['pre1'])
            self.xas_pre2.SetValue(opts['pre2'])
            self.xas_nor1.SetValue(opts['norm1'])
            self.xas_nor2.SetValue(opts['norm2'])
            self.xas_vict.SetSelection(opts['nvict'])
            self.xas_nnor.SetSelection(opts['nnorm'])
            self.xas_showe0.SetValue(opts['show_e0'])
            self.xas_autoe0.SetValue(opts['auto_e0'])
            self.xas_autostep.SetValue(opts['auto_step'])

    def build_display(self):
        self.SetFont(Font(10))
        titleopts = dict(font=Font(11), colour='#AA0000')

        gopts = dict(ncols=4, nrows=4, pad=2, itemstyle=LCEN)
        xas = self.xaspanel = GridPanel(self, **gopts)
        gen = self.genpanel = GridPanel(self, **gopts)
        self.btns = {}
        #gen
        opts  = dict(action=self.UpdatePlot, size=(65, -1), gformat=True)

        self.xshift = FloatCtrl(gen, value=0.0, **opts)
        self.xscale = FloatCtrl(gen, value=1.0, **opts)

        self.yshift = FloatCtrl(gen, value=0.0, **opts)
        self.yscale = FloatCtrl(gen, value=1.0, **opts)

        self.btns['xshift'] = BitmapButton(gen, get_icon('plus'),
                                           action=partial(self.on_selpoint, opt='xshift'),
                                           tooltip='use last point selected from plot')
        self.btns['yshift'] = BitmapButton(gen, get_icon('plus'),
                                           action=partial(self.on_selpoint, opt='yshift'),
                                           tooltip='use last point selected from plot')

        opts  = dict(action=self.onSmoothChoice, size=(30, -1))
        sm_row1 = wx.Panel(gen)
        sm_row2 = wx.Panel(gen)
        sm_siz1= wx.BoxSizer(wx.HORIZONTAL)
        sm_siz2= wx.BoxSizer(wx.HORIZONTAL)

        self.smooth_c0 = FloatCtrl(sm_row1, value=2, precision=0, minval=1, **opts)
        self.smooth_c1 = FloatCtrl(sm_row1, value=1, precision=0, minval=1, **opts)
        self.smooth_msg = SimpleText(sm_row1, label='         ', size=(205, -1))
        opts['size'] =  (65, -1)
        self.smooth_sig = FloatCtrl(sm_row2, value=1, gformat=True, **opts)

        opts['size'] =  (120, -1)
        self.smooth_op = Choice(sm_row1, choices=SMOOTH_OPS, **opts)
        self.smooth_op.SetSelection(0)

        self.smooth_conv = Choice(sm_row2, choices=CONV_OPS, **opts)

        self.smooth_c0.Disable()
        self.smooth_c1.Disable()
        self.smooth_sig.Disable()
        self.smooth_conv.SetSelection(0)
        self.smooth_conv.Disable()

        sm_siz1.Add(self.smooth_op,  0, LCEN, 1)
        sm_siz1.Add(SimpleText(sm_row1, ' n= '), 0, LCEN, 1)
        sm_siz1.Add(self.smooth_c0,  0, LCEN, 1)
        sm_siz1.Add(SimpleText(sm_row1, ' order= '), 0, LCEN, 1)
        sm_siz1.Add(self.smooth_c1,  0, LCEN, 1)
        sm_siz1.Add(self.smooth_msg, 0, LCEN, 1)

        sm_siz2.Add(SimpleText(sm_row2, ' form= '), 0, LCEN, 1)
        sm_siz2.Add(self.smooth_conv,  0, LCEN, 1)
        sm_siz2.Add(SimpleText(sm_row2, ' sigma= '), 0, LCEN, 1)
        sm_siz2.Add(self.smooth_sig,  0, LCEN, 1)
        pack(sm_row1, sm_siz1)
        pack(sm_row2, sm_siz2)

        gen.Add(SimpleText(gen, ' General Data Processing', **titleopts), dcol=8)
        gen.Add(SimpleText(gen, ' X shift:'),  newrow=True)
        gen.Add(self.btns['xshift'])
        gen.Add(self.xshift, dcol=2)
        gen.Add(SimpleText(gen, ' X scale:'))
        gen.Add(self.xscale, dcol=2)

        gen.Add(SimpleText(gen, ' Y shift:'),  newrow=True)
        gen.Add(self.btns['yshift'])
        gen.Add(self.yshift, dcol=2)
        gen.Add(SimpleText(gen, ' Y scale:'))
        gen.Add(self.yscale, dcol=2)

        gen.Add(SimpleText(gen, ' Smoothing:'), newrow=True)
        gen.Add(sm_row1, dcol=8)
        gen.Add(sm_row2, icol=1, dcol=7, newrow=True)

        gen.pack()

        #xas
        opts = {'action': self.UpdatePlot}
        e0opts_panel = wx.Panel(xas)
        self.xas_autoe0   = Check(e0opts_panel, default=True, label='auto?', **opts)
        self.xas_showe0   = Check(e0opts_panel, default=True, label='show?', **opts)
        sx = wx.BoxSizer(wx.HORIZONTAL)
        sx.Add(self.xas_autoe0, 0, LCEN, 4)
        sx.Add(self.xas_showe0, 0, LCEN, 4)
        pack(e0opts_panel, sx)

        self.xas_autostep = Check(xas, default=True, label='auto?', **opts)
        opts['size'] = (250, -1)
        self.xas_op  = Choice(xas, choices=XASOPChoices,  **opts)

        self.xas_op.SetStringSelection('Normalized')

        for name in ('e0', 'pre1', 'pre2', 'nor1', 'nor2'):
            bb = BitmapButton(xas, get_icon('plus'),
                              action=partial(self.on_selpoint, opt=name),
                              tooltip='use last point selected from plot')
            self.btns[name] = bb

        opts = {'size': (65, -1), 'gformat': True}

        self.xas_e0   = FloatCtrl(xas, value=0, action=self.onSet_XASE0, **opts)
        self.xas_step = FloatCtrl(xas, value=0, action=self.onSet_XASStep, **opts)

        opts['precision'] = 1
        opts['action'] =  self.UpdatePlot
        self.xas_pre1 = FloatCtrl(xas, value=-200, **opts)
        self.xas_pre2 = FloatCtrl(xas, value= -30, **opts)
        self.xas_nor1 = FloatCtrl(xas, value=  50, **opts)
        self.xas_nor2 = FloatCtrl(xas, value= -50, **opts)

        opts = {'size': (50, -1),
                'choices': ('0', '1', '2', '3'),
                'action': self.UpdatePlot}
        self.xas_vict = Choice(xas, **opts)
        self.xas_nnor = Choice(xas, **opts)
        self.xas_vict.SetSelection(1)
        self.xas_nnor.SetSelection(1)

        def CopyBtn(name):
            return Button(xas, 'Copy', size=(50, 30),
                          action=partial(self.onCopyParam, name))


        xas.Add(SimpleText(xas, ' XAS Data Processing', **titleopts), dcol=6)
        xas.Add(SimpleText(xas, ' Copy to Selected Groups?'), style=RCEN, dcol=3)
        xas.Add(SimpleText(xas, 'Arrays to Plot: '),  newrow=True)
        xas.Add(self.xas_op,  dcol=6)
        xas.Add((10, 10))
        xas.Add(CopyBtn('xas_op'), style=RCEN)

        xas.Add(SimpleText(xas, 'E0 : '), newrow=True)
        xas.Add(self.btns['e0'])
        xas.Add(self.xas_e0)
        xas.Add(e0opts_panel, dcol=4)
        xas.Add((10, 1))
        xas.Add(CopyBtn('xas_e0'), style=RCEN)

        xas.Add(SimpleText(xas, 'Edge Step: '), newrow=True)
        xas.Add((10, 1))
        xas.Add(self.xas_step)
        xas.Add(self.xas_autostep, dcol=3)
        xas.Add((10, 1))
        xas.Add((10, 1))
        xas.Add(CopyBtn('xas_step'), style=RCEN)

        xas.Add(SimpleText(xas, 'Pre-edge range: '), newrow=True)
        xas.Add(self.btns['pre1'])
        xas.Add(self.xas_pre1)
        xas.Add(SimpleText(xas, ':'))
        xas.Add(self.btns['pre2'])
        xas.Add(self.xas_pre2)
        xas.Add(SimpleText(xas, 'Victoreen:'))
        xas.Add(self.xas_vict)
        xas.Add(CopyBtn('xas_pre'), style=RCEN)

        xas.Add(SimpleText(xas, 'Normalization range: '), newrow=True)
        xas.Add(self.btns['nor1'])
        xas.Add(self.xas_nor1)
        xas.Add(SimpleText(xas, ':'))
        xas.Add(self.btns['nor2'])
        xas.Add(self.xas_nor2)
        xas.Add(SimpleText(xas, 'PolyOrder:'))
        xas.Add(self.xas_nnor)
        xas.Add(CopyBtn('xas_norm'), style=RCEN)

        xas.pack()

        saveconf = Button(self, 'Save as Default Settings',
                          size=(175, 30),
                          action=self.onSaveConfigBtn)

        hxline = HLine(self, size=(550, 2))

        sizer = wx.BoxSizer(wx.VERTICAL)

        sizer.AddMany([((10, 10), 0, LCEN, 10), (gen,      0, LCEN, 10),
                       ((10, 10), 0, LCEN, 10), (hxline,   0, LCEN, 10),
                       ((10, 10), 0, LCEN, 10), (xas,      0, LCEN, 10),
                       ((10, 10), 0, LCEN, 10), (saveconf, 0, LCEN, 10),
                       ])

        xas.Disable()

        pack(self, sizer)

    def onSaveConfigBtn(self, evt=None):
        conf = self.controller.larch.symtable._sys.xyfit

        data_proc = {}
        data_proc.update(getattr(conf, 'data_proc', {}))

        data_proc['xshift'] = self.xshift.GetValue()
        data_proc['yshift'] = self.yshift.GetValue()
        data_proc['xscale'] = self.xscale.GetValue()
        data_proc['yscale'] = self.yscale.GetValue()
        data_proc['smooth_op'] = str(self.smooth_op.GetStringSelection())
        data_proc['smooth_c0'] = int(self.smooth_c0.GetValue())
        data_proc['smooth_c1'] = int(self.smooth_c1.GetValue())
        data_proc['smooth_sig'] = float(self.smooth_sig.GetValue())
        data_proc['smooth_conv'] = str(self.smooth_conv.GetStringSelection())

        conf.data_proc = data_proc

        if self.xaspanel.Enabled:
            xas_proc = {}
            xas_proc.update(getattr(conf, 'xas_proc', {}))

            xas_proc['auto_e0'] = True
            xas_proc['auto_step'] = True

            xas_proc['pre1']  = self.xas_pre1.GetValue()
            xas_proc['pre2']  = self.xas_pre2.GetValue()
            xas_proc['norm1'] = self.xas_nor1.GetValue()
            xas_proc['norm2'] = self.xas_nor2.GetValue()
            xas_proc['nvict'] = self.xas_vict.GetSelection()
            xas_proc['nnorm'] = self.xas_nnor.GetSelection()

            xas_proc['show_e0'] = self.xas_showe0.IsChecked()
            xas_proc['nnorm'] = int(self.xas_nnor.GetSelection())
            xas_proc['nvict'] = int(self.xas_vict.GetSelection())
            xas_proc['xas_op'] = str(self.xas_op.GetStringSelection())
            conf.xas_proc = xas_proc

    def onCopyParam(self, name=None, event=None):
        proc_opts = self.controller.group.proc_opts
        opts = {}
        name = str(name)
        if name == 'xas_op':
            opts['xas_op'] = proc_opts['xas_op']
        elif name == 'xas_e0':
            opts['e0'] = proc_opts['e0']
            opts['show_e0'] = proc_opts['show_e0']
            opts['auto_e0'] = False
        elif name == 'xas_step':
            opts['edge_step'] = proc_opts['edge_step']
            opts['auto_step'] = False
        elif name == 'xas_pre':
            opts['nvict'] = proc_opts['nvict']
            opts['pre1'] = proc_opts['pre1']
            opts['pre2'] = proc_opts['pre2']
        elif name == 'xas_norm':
            opts['nnorm'] = proc_opts['nnorm']
            opts['norm1'] = proc_opts['norm1']
            opts['norm2'] = proc_opts['norm2']

        for checked in self.controller.filelist.GetCheckedStrings():
            groupname = self.controller.file_groups[str(checked)]
            grp = self.controller.get_group(groupname)
            if grp != self.controller.group:
                grp.proc_opts.update(opts)
                self.fill(grp)
                self.process(grp.groupname)

    def onSmoothChoice(self, evt=None, value=1):
        try:
            choice = self.smooth_op.GetStringSelection().lower()
            conv  = self.smooth_conv.GetStringSelection()
            self.smooth_c0.Disable()
            self.smooth_c1.Disable()
            self.smooth_conv.Disable()
            self.smooth_sig.Disable()
            self.smooth_msg.SetLabel('')
            self.smooth_c0.SetMin(1)
            self.smooth_c0.odd_only = False
            if choice.startswith('box'):
                self.smooth_c0.Enable()
            elif choice.startswith('savi'):
                self.smooth_c0.Enable()
                self.smooth_c1.Enable()
                self.smooth_c0.Enable()
                self.smooth_c0.odd_only = True

                c0 = int(self.smooth_c0.GetValue())
                c1 = int(self.smooth_c1.GetValue())
                x0 = max(c1+1, c0)
                if x0 % 2 == 0:
                    x0 += 1
                self.smooth_c0.SetMin(c1+1)
                if c0 != x0:
                    self.smooth_c0.SetValue(x0)
                self.smooth_msg.SetLabel('n must odd and  > order+1')

            elif choice.startswith('conv'):
                self.smooth_conv.Enable()
                self.smooth_sig.Enable()
            self.needs_update = True
        except AttributeError:
            pass

    def onSet_XASE0(self, evt=None, **kws):
        self.xas_autoe0.SetValue(0)
        self.needs_update = True

    def onSet_XASStep(self, evt=None, **kws):
        self.xas_autostep.SetValue(0)
        self.needs_update = True

    def onProcessTimer(self, evt=None):
        if self.needs_update and self.controller.groupname is not None:
            self.process(self.controller.groupname)
            self.controller.plot_group(groupname=self.controller.groupname, new=True)
            self.needs_update = False

    def UpdatePlot(self, evt=None, **kws):
        self.needs_update = True

    def on_selpoint(self, evt=None, opt='e0'):
        xval, yval = self.controller.get_cursor()
        if xval is None:
            return

        e0 = self.xas_e0.GetValue()
        if opt == 'e0':
            self.xas_e0.SetValue(xval)
            self.xas_autoe0.SetValue(0)
        elif opt == 'pre1':
            self.xas_pre1.SetValue(xval-e0)
        elif opt == 'pre2':
            self.xas_pre2.SetValue(xval-e0)
        elif opt == 'nor1':
            self.xas_nor1.SetValue(xval-e0)
        elif opt == 'nor2':
            self.xas_nor2.SetValue(xval-e0)
        elif opt == 'xshift':
            self.xshift.SetValue(xval)
        elif opt == 'yshift':
            self.yshift.SetValue(yval)

    def process(self, gname,  **kws):
        """ handle process (pre-edge/normalize) XAS data from XAS form, overwriting
        larch group 'x' and 'y' attributes to be plotted
        """
        dgroup = self.controller.get_group(gname)
        proc_opts = {}

        proc_opts['xshift'] = self.xshift.GetValue()
        proc_opts['yshift'] = self.yshift.GetValue()
        proc_opts['xscale'] = self.xscale.GetValue()
        proc_opts['yscale'] = self.yscale.GetValue()
        proc_opts['smooth_op'] = self.smooth_op.GetStringSelection()
        proc_opts['smooth_c0'] = int(self.smooth_c0.GetValue())
        proc_opts['smooth_c1'] = int(self.smooth_c1.GetValue())
        proc_opts['smooth_sig'] = float(self.smooth_sig.GetValue())
        proc_opts['smooth_conv'] = self.smooth_conv.GetStringSelection()

        self.xaspanel.Enable(dgroup.datatype.startswith('xas'))
        if dgroup.datatype.startswith('xas'):
            proc_opts['datatype'] = 'xas'
            proc_opts['e0'] = self.xas_e0.GetValue()
            proc_opts['edge_step'] = self.xas_step.GetValue()
            proc_opts['pre1']  = self.xas_pre1.GetValue()
            proc_opts['pre2']  = self.xas_pre2.GetValue()
            proc_opts['norm1'] = self.xas_nor1.GetValue()
            proc_opts['norm2'] = self.xas_nor2.GetValue()
            proc_opts['nvict'] = self.xas_vict.GetSelection()
            proc_opts['nnorm'] = self.xas_nnor.GetSelection()

            proc_opts['auto_e0'] = self.xas_autoe0.IsChecked()
            proc_opts['show_e0'] = self.xas_showe0.IsChecked()
            proc_opts['auto_step'] = self.xas_autostep.IsChecked()
            proc_opts['nnorm'] = int(self.xas_nnor.GetSelection())
            proc_opts['nvict'] = int(self.xas_vict.GetSelection())
            proc_opts['xas_op'] = self.xas_op.GetStringSelection()

        self.controller.process(dgroup, proc_opts=proc_opts)

        if dgroup.datatype.startswith('xas'):

            if self.xas_autoe0.IsChecked():
                self.xas_e0.SetValue(dgroup.proc_opts['e0'], act=False)
            if self.xas_autostep.IsChecked():
                self.xas_step.SetValue(dgroup.proc_opts['edge_step'], act=False)

            self.xas_pre1.SetValue(dgroup.proc_opts['pre1'])
            self.xas_pre2.SetValue(dgroup.proc_opts['pre2'])
            self.xas_nor1.SetValue(dgroup.proc_opts['norm1'])
            self.xas_nor2.SetValue(dgroup.proc_opts['norm2'])

            dgroup.orig_ylabel = dgroup.plot_ylabel
            dgroup.plot_ylabel = '$\mu$'
            dgroup.plot_y2label = None
            dgroup.plot_xlabel = '$E \,\mathrm{(eV)}$'
            dgroup.plot_yarrays = [(dgroup.mu, PLOTOPTS_1, dgroup.plot_ylabel)]
            y4e0 = dgroup.mu

            out = self.xas_op.GetStringSelection().lower() # raw, pre, norm, flat
            if out.startswith('raw data + pre'):
                dgroup.plot_yarrays = [(dgroup.mu,        PLOTOPTS_1, '$\mu$'),
                                       (dgroup.pre_edge,  PLOTOPTS_2, 'pre edge'),
                                       (dgroup.post_edge, PLOTOPTS_2, 'post edge')]
            elif out.startswith('pre'):
                dgroup.pre_edge_sub = dgroup.norm * dgroup.edge_step
                dgroup.plot_yarrays = [(dgroup.pre_edge_sub, PLOTOPTS_1,
                                        'pre-edge subtracted $\mu$')]
                y4e0 = dgroup.pre_edge_sub
                dgroup.plot_ylabel = 'pre-edge subtracted $\mu$'
            elif 'norm' in out and 'deriv' in out:
                dgroup.plot_yarrays = [(dgroup.norm, PLOTOPTS_1, 'normalized $\mu$'),
                                       (dgroup.dmude, PLOTOPTS_D, '$d\mu/dE$')]
                y4e0 = dgroup.norm
                dgroup.plot_ylabel = 'normalized $\mu$'
                dgroup.plot_y2label = '$d\mu/dE$'
                dgroup.y = dgroup.norm

            elif out.startswith('norm'):
                dgroup.plot_yarrays = [(dgroup.norm, PLOTOPTS_1, 'normalized $\mu$')]
                y4e0 = dgroup.norm
                dgroup.plot_ylabel = 'normalized $\mu$'
                dgroup.y = dgroup.norm

            elif out.startswith('deriv'):
                dgroup.plot_yarrays = [(dgroup.dmude, PLOTOPTS_1, '$d\mu/dE$')]
                y4e0 = dgroup.dmude
                dgroup.plot_ylabel = '$d\mu/dE$'
                dgroup.y = dgroup.dmude

            dgroup.plot_ymarkers = []
            if self.xas_showe0.IsChecked():
                ie0 = index_of(dgroup.xdat, dgroup.e0)
                dgroup.plot_ymarkers = [(dgroup.e0, y4e0[ie0], {'label': '_nolegend_'})]
Esempio n. 4
0
class XASNormPanel(wx.Panel):
    """XAS normalization Panel"""
    def __init__(self, parent, controller=None, reporter=None, **kws):
        wx.Panel.__init__(self, parent, -1, **kws)
        self.parent = parent
        self.controller = controller
        self.reporter = reporter
        self.skip_process = False
        self.build_display()

    def onPanelExposed(self, **kws):
        # called when notebook is selected
        try:
            fname = self.controller.filelist.GetStringSelection()
            gname = self.controller.file_groups[fname]
            dgroup = self.controller.get_group(gname)
            self.fill_form(dgroup)
        except:
            pass

    def larch_eval(self, cmd):
        """eval"""
        self.controller.larch.eval(cmd)

    def build_display(self):
        self.SetFont(Font(10))
        titleopts = dict(font=Font(11), colour='#AA0000')

        xas = self.xaspanel = GridPanel(self,
                                        ncols=4,
                                        nrows=4,
                                        pad=2,
                                        itemstyle=LCEN)

        self.plotone_op = Choice(xas,
                                 choices=list(PlotOne_Choices.keys()),
                                 action=self.onPlotOne,
                                 size=(200, -1))
        self.plotsel_op = Choice(xas,
                                 choices=list(PlotSel_Choices.keys()),
                                 action=self.onPlotSel,
                                 size=(200, -1))

        self.plotone_op.SetStringSelection('Normalized')
        self.plotsel_op.SetStringSelection('Normalized')

        plot_one = Button(xas,
                          'Plot This Group',
                          size=(150, -1),
                          action=self.onPlotOne)

        plot_sel = Button(xas,
                          'Plot Selected Groups',
                          size=(150, -1),
                          action=self.onPlotSel)

        self.btns = {}

        opts = dict(action=self.onReprocess)

        self.deconv_ewid = FloatCtrl(xas,
                                     value=0.5,
                                     precision=2,
                                     minval=0,
                                     size=(50, -1),
                                     **opts)

        self.deconv_form = Choice(xas,
                                  choices=DECONV_OPS,
                                  size=(100, -1),
                                  **opts)

        e0opts_panel = wx.Panel(xas)
        self.xas_autoe0 = Check(e0opts_panel,
                                default=True,
                                label='auto?',
                                **opts)
        self.xas_showe0 = Check(e0opts_panel,
                                default=True,
                                label='show?',
                                **opts)
        sx = wx.BoxSizer(wx.HORIZONTAL)
        sx.Add(self.xas_autoe0, 0, LCEN, 4)
        sx.Add(self.xas_showe0, 0, LCEN, 4)
        pack(e0opts_panel, sx)

        self.xas_autostep = Check(xas, default=True, label='auto?', **opts)

        for name in ('e0', 'pre1', 'pre2', 'nor1', 'nor2'):
            bb = BitmapButton(xas,
                              get_icon('plus'),
                              action=partial(self.on_selpoint, opt=name),
                              tooltip='use last point selected from plot')
            self.btns[name] = bb

        opts['size'] = (50, -1)
        self.xas_vict = Choice(xas, choices=('0', '1', '2', '3'), **opts)
        self.xas_nnor = Choice(xas, choices=('0', '1', '2', '3'), **opts)
        self.xas_vict.SetSelection(1)
        self.xas_nnor.SetSelection(1)

        opts.update({'size': (75, -1), 'precision': 1})

        self.xas_pre1 = FloatCtrl(xas, value=-np.inf, **opts)
        self.xas_pre2 = FloatCtrl(xas, value=-30, **opts)
        self.xas_nor1 = FloatCtrl(xas, value=50, **opts)
        self.xas_nor2 = FloatCtrl(xas, value=np.inf, **opts)

        opts = {'size': (75, -1), 'gformat': True}
        self.xas_e0 = FloatCtrl(xas, value=0, action=self.onSet_XASE0, **opts)
        self.xas_step = FloatCtrl(xas,
                                  value=0,
                                  action=self.onSet_XASStep,
                                  **opts)

        saveconf = Button(xas,
                          'Save as Default Settings',
                          size=(200, -1),
                          action=self.onSaveConfigBtn)

        def CopyBtn(name):
            return Button(xas,
                          'Copy',
                          size=(50, -1),
                          action=partial(self.onCopyParam, name))

        xas.Add(SimpleText(xas, ' XAS Pre-edge subtraction and Normalization',
                           **titleopts),
                dcol=6)
        xas.Add(SimpleText(xas, ' Copy to Selected Groups?'),
                style=RCEN,
                dcol=3)

        xas.Add(plot_sel, newrow=True)
        xas.Add(self.plotsel_op, dcol=6)

        xas.Add(plot_one, newrow=True)
        xas.Add(self.plotone_op, dcol=6)
        xas.Add((10, 10))
        xas.Add(CopyBtn('plotone_op'), style=RCEN)

        xas.Add(SimpleText(xas, 'E0 : '), newrow=True)
        xas.Add(self.btns['e0'])
        xas.Add(self.xas_e0)
        xas.Add(e0opts_panel, dcol=4)
        xas.Add((10, 1))
        xas.Add(CopyBtn('xas_e0'), style=RCEN)

        xas.Add(SimpleText(xas, 'Edge Step: '), newrow=True)
        xas.Add((10, 1))
        xas.Add(self.xas_step)
        xas.Add(self.xas_autostep, dcol=3)
        xas.Add((10, 1))
        xas.Add((10, 1))
        xas.Add(CopyBtn('xas_step'), style=RCEN)

        xas.Add(SimpleText(xas, 'Pre-edge range: '), newrow=True)
        xas.Add(self.btns['pre1'])
        xas.Add(self.xas_pre1)
        xas.Add(SimpleText(xas, ':'))
        xas.Add(self.btns['pre2'])
        xas.Add(self.xas_pre2)
        xas.Add(SimpleText(xas, 'Victoreen:'))
        xas.Add(self.xas_vict)
        xas.Add(CopyBtn('xas_pre'), style=RCEN)

        xas.Add(SimpleText(xas, 'Normalization range: '), newrow=True)
        xas.Add(self.btns['nor1'])
        xas.Add(self.xas_nor1)
        xas.Add(SimpleText(xas, ':'))
        xas.Add(self.btns['nor2'])
        xas.Add(self.xas_nor2)
        xas.Add(SimpleText(xas, 'Poly Order:'))
        xas.Add(self.xas_nnor)
        xas.Add(CopyBtn('xas_norm'), style=RCEN)

        xas.Add(SimpleText(xas, ' Deconvolution:'), newrow=True)
        xas.Add(self.deconv_form, dcol=5)
        xas.Add(SimpleText(xas, 'Energy width:'))
        xas.Add(self.deconv_ewid)
        xas.Add(CopyBtn('deconv'), style=RCEN)

        xas.Add(saveconf, dcol=6, newrow=True)
        xas.pack()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add((5, 5), 0, LCEN, 3)
        sizer.Add(HLine(self, size=(550, 2)), 0, LCEN, 3)
        sizer.Add(xas, 0, LCEN, 3)
        sizer.Add((5, 5), 0, LCEN, 3)
        sizer.Add(HLine(self, size=(550, 2)), 0, LCEN, 3)
        pack(self, sizer)

    def get_config(self, dgroup=None):
        """get processing configuration for a group"""
        if dgroup is None:
            dgroup = self.controller.get_group()

        conf = getattr(dgroup, 'xasnorm_config', {})
        if 'e0' not in conf:
            conf = default_xasnorm_config()
        dgroup.xasnorm_config = conf
        return conf

    def fill_form(self, dgroup):
        """fill in form from a data group"""
        opts = self.get_config(dgroup)
        self.skip_process = True

        widlist = (self.xas_e0, self.xas_step, self.xas_pre1, self.xas_pre2,
                   self.xas_nor1, self.xas_nor2, self.xas_vict, self.xas_nnor,
                   self.xas_showe0, self.xas_autoe0, self.xas_autostep,
                   self.deconv_form, self.deconv_ewid)

        if dgroup.datatype == 'xas':
            for k in widlist:
                k.Enable()

            self.plotone_op.SetChoices(list(PlotOne_Choices.keys()))
            self.plotsel_op.SetChoices(list(PlotSel_Choices.keys()))

            self.plotone_op.SetStringSelection(opts['plotone_op'])
            self.plotsel_op.SetStringSelection(opts['plotsel_op'])
            self.xas_e0.SetValue(opts['e0'])
            self.xas_step.SetValue(opts['edge_step'])
            self.xas_pre1.SetValue(opts['pre1'])
            self.xas_pre2.SetValue(opts['pre2'])
            self.xas_nor1.SetValue(opts['norm1'])
            self.xas_nor2.SetValue(opts['norm2'])
            self.xas_vict.SetSelection(opts['nvict'])
            self.xas_nnor.SetSelection(opts['nnorm'])
            self.xas_showe0.SetValue(opts['show_e0'])
            self.xas_autoe0.SetValue(opts['auto_e0'])
            self.xas_autostep.SetValue(opts['auto_step'])
            self.deconv_form.SetStringSelection(opts['deconv_form'])
            self.deconv_ewid.SetValue(opts['deconv_ewid'])
        else:
            self.plotone_op.SetChoices(list(PlotOne_Choices_nonxas.keys()))
            self.plotsel_op.SetChoices(list(PlotSel_Choices_nonxas.keys()))
            self.plotone_op.SetStringSelection('Raw Data')
            self.plotsel_op.SetStringSelection('Raw Data')
            for k in widlist:
                k.Disable()

        self.skip_process = False
        self.process(dgroup)

    def read_form(self):
        "read for, returning dict of values"
        form_opts = {}
        form_opts['e0'] = self.xas_e0.GetValue()
        form_opts['edge_step'] = self.xas_step.GetValue()
        form_opts['pre1'] = self.xas_pre1.GetValue()
        form_opts['pre2'] = self.xas_pre2.GetValue()
        form_opts['norm1'] = self.xas_nor1.GetValue()
        form_opts['norm2'] = self.xas_nor2.GetValue()
        form_opts['nnorm'] = int(self.xas_nnor.GetSelection())
        form_opts['nvict'] = int(self.xas_vict.GetSelection())

        form_opts['plotone_op'] = self.plotone_op.GetStringSelection()
        form_opts['plotsel_op'] = self.plotsel_op.GetStringSelection()

        form_opts['show_e0'] = self.xas_showe0.IsChecked()
        form_opts['auto_e0'] = self.xas_autoe0.IsChecked()
        form_opts['auto_step'] = self.xas_autostep.IsChecked()

        form_opts['deconv_form'] = self.deconv_form.GetStringSelection()
        form_opts['deconv_ewid'] = self.deconv_ewid.GetValue()
        return form_opts

    def onPlotOne(self, evt=None):
        self.plot(self.controller.get_group())

    def onPlotSel(self, evt=None):
        newplot = True
        group_ids = self.controller.filelist.GetCheckedStrings()
        if len(group_ids) < 1:
            return
        last_id = group_ids[-1]

        yarray_name = PlotSel_Choices[self.plotsel_op.GetStringSelection()]
        ylabel = getattr(plotlabels, yarray_name)

        # print("Plot Sel:: ", group_ids)
        for checked in group_ids:
            groupname = self.controller.file_groups[str(checked)]
            dgroup = self.controller.get_group(groupname)
            plot_yarrays = [(yarray_name, PLOTOPTS_1, dgroup.filename)]
            dgroup.plot_ylabel = ylabel
            if dgroup is not None:
                dgroup.plot_extras = []
                # print(" PlotSel -> plot ", checked, (last_id!=checked) )
                self.plot(dgroup,
                          title='',
                          new=newplot,
                          plot_yarrays=plot_yarrays,
                          show_legend=True,
                          with_extras=False,
                          delay_draw=(last_id != checked))
                newplot = False
        self.controller.get_display(stacked=False).panel.canvas.draw()

    def onSaveConfigBtn(self, evt=None):
        conf = self.controller.larch.symtable._sys.xas_viewer
        opts = {}
        opts.update(getattr(conf, 'xas_norm', {}))
        opts.update(self.read_form())
        conf.xas_norm = opts

    def onCopyParam(self, name=None, evt=None):
        conf = self.get_config()
        conf.update(self.read_form())
        opts = {}
        name = str(name)

        def copy_attrs(*args):
            for a in args:
                opts[a] = conf[a]

        if name == 'plotone_op':
            copy_attrs('plotone_op')
        elif name == 'xas_e0':
            copy_attrs('e0', 'show_e0')
            opts['auto_e0'] = False
        elif name == 'xas_step':
            copy_attrs('edge_step')
            opts['auto_step'] = False
        elif name == 'xas_pre':
            copy_attrs('pre1', 'pre2', 'nvict')
        elif name == 'xas_norm':
            copy_attrs('nnorm', 'norm1', 'norm2')
        elif name == 'deconv':
            copy_attrs('deconv_form', 'deconv_ewid')

        for checked in self.controller.filelist.GetCheckedStrings():
            groupname = self.controller.file_groups[str(checked)]
            grp = self.controller.get_group(groupname)
            if grp != self.controller.group:
                grp.xasnorm_config.update(opts)
                self.fill_form(grp)
                self.process(grp)

    def onSet_XASE0(self, evt=None, value=None):
        self.xas_autoe0.SetValue(0)
        self.onReprocess()

    def onSet_XASStep(self, evt=None, value=None):
        self.xas_autostep.SetValue(0)
        self.onReprocess()

    def onReprocess(self, evt=None, value=None, **kws):
        if self.skip_process:
            return
        try:
            dgroup = self.controller.get_group()
        except TypeError:
            return
        self.process(dgroup)
        self.plot(dgroup)

    def on_selpoint(self, evt=None, opt='e0'):
        xval, yval = self.controller.get_cursor()
        if xval is None:
            return

        e0 = self.xas_e0.GetValue()
        if opt == 'e0':
            self.xas_e0.SetValue(xval)
            self.xas_autoe0.SetValue(0)
        elif opt == 'pre1':
            self.xas_pre1.SetValue(xval - e0)
        elif opt == 'pre2':
            self.xas_pre2.SetValue(xval - e0)
        elif opt == 'nor1':
            self.xas_nor1.SetValue(xval - e0)
        elif opt == 'nor2':
            self.xas_nor2.SetValue(xval - e0)
        else:
            print(" unknown selection point ", opt)

    def process(self, dgroup, **kws):
        """ handle process (pre-edge/normalize, deconvolve) of XAS data from XAS form
        """
        if self.skip_process:
            return
        self.skip_process = True

        dgroup.custom_plotopts = {}
        # print("XAS norm process ", dgroup.datatype)

        if dgroup.datatype != 'xas':
            self.skip_process = False
            dgroup.mu = dgroup.ydat * 1.0
            return

        en_units = getattr(dgroup, 'energy_units', None)
        if en_units is None:
            en_units = 'eV'
            units = guess_energy_units(dgroup.energy)

            if units != 'eV':
                dlg = EnergyUnitsDialog(self.parent, units, dgroup.energy)
                res = dlg.GetResponse()
                dlg.Destroy()
                if res.ok:
                    en_units = res.units
                    dgroup.xdat = dgroup.energy = res.energy
            dgroup.energy_units = en_units

        form = self.read_form()
        e0 = form['e0']
        edge_step = form['edge_step']

        form['group'] = dgroup.groupname

        copts = [dgroup.groupname]
        if not form['auto_e0']:
            if e0 < max(dgroup.energy) and e0 > min(dgroup.energy):
                copts.append("e0=%.4f" % float(e0))

        if not form['auto_step']:
            copts.append("step=%.4f" % float(edge_step))

        for attr in ('pre1', 'pre2', 'nvict', 'nnorm', 'norm1', 'norm2'):
            copts.append("%s=%.2f" % (attr, form[attr]))

        self.larch_eval("pre_edge(%s)" % (', '.join(copts)))

        if form['auto_e0']:
            self.xas_e0.SetValue(dgroup.e0, act=False)
        if form['auto_step']:
            self.xas_step.SetValue(dgroup.edge_step, act=False)

        self.xas_pre1.SetValue(dgroup.pre_edge_details.pre1)
        self.xas_pre2.SetValue(dgroup.pre_edge_details.pre2)
        self.xas_nor1.SetValue(dgroup.pre_edge_details.norm1)
        self.xas_nor2.SetValue(dgroup.pre_edge_details.norm2)

        # deconvolution
        deconv_form = form['deconv_form'].lower()
        deconv_ewid = float(form['deconv_ewid'])
        if not deconv_form.startswith('none') and deconv_ewid > 1.e-3:
            dopts = [
                dgroup.groupname,
                "form='%s'" % (deconv_form),
                "esigma=%.4f" % (deconv_ewid)
            ]
            self.larch_eval("xas_deconvolve(%s)" % (', '.join(dopts)))

        for attr in ('e0', 'edge_step'):
            dgroup.xasnorm_config[attr] = getattr(dgroup, attr)

        for attr in ('pre1', 'pre2', 'nnorm', 'norm1', 'norm2'):
            dgroup.xasnorm_config[attr] = getattr(dgroup.pre_edge_details,
                                                  attr)

        self.skip_process = False

    def get_plot_arrays(self, dgroup):
        form = self.read_form()

        lab = plotlabels.norm
        dgroup.plot_y2label = None
        dgroup.plot_xlabel = plotlabels.energy
        dgroup.plot_yarrays = [('norm', PLOTOPTS_1, lab)]

        if dgroup.datatype != 'xas':
            pchoice = PlotOne_Choices_nonxas[
                self.plotone_op.GetStringSelection()]
            dgroup.plot_xlabel = 'x'
            dgroup.plot_ylabel = 'y'
            dgroup.plot_yarrays = [('ydat', PLOTOPTS_1, 'ydat')]
            dgroup.dmude = np.gradient(dgroup.ydat) / np.gradient(dgroup.xdat)
            if pchoice == 'dmude':
                dgroup.plot_ylabel = 'dy/dx'
                dgroup.plot_yarrays = [('dmude', PLOTOPTS_1, 'dy/dx')]
            elif pchoice == 'norm+deriv':
                lab = plotlabels.norm
                dgroup.plot_y2label = 'dy/dx'
                dgroup.plot_yarrays = [('ydat', PLOTOPTS_1, 'y'),
                                       ('dmude', PLOTOPTS_D, 'dy/dx')]
            return

        pchoice = PlotOne_Choices[self.plotone_op.GetStringSelection()]
        if pchoice in ('mu', 'norm', 'flat', 'dmude'):
            lab = getattr(plotlabels, pchoice)
            dgroup.plot_yarrays = [(pchoice, PLOTOPTS_1, lab)]

        elif pchoice == 'prelines':
            dgroup.plot_yarrays = [('mu', PLOTOPTS_1, lab),
                                   ('pre_edge', PLOTOPTS_2, 'pre edge'),
                                   ('post_edge', PLOTOPTS_2, 'post edge')]
        elif pchoice == 'preedge':
            dgroup.pre_edge_sub = dgroup.norm * dgroup.edge_step
            dgroup.plot_yarrays = [('pre_edge_sub', PLOTOPTS_1,
                                    r'pre-edge subtracted $\mu$')]
            lab = r'pre-edge subtracted $\mu$'

        elif pchoice == 'norm+deriv':
            lab = plotlabels.norm
            lab2 = plotlabels.dmude
            dgroup.plot_yarrays = [('norm', PLOTOPTS_1, lab),
                                   ('dmude', PLOTOPTS_D, lab2)]
            dgroup.plot_y2label = lab2

        elif pchoice == 'deconv' and hasattr(dgroup, 'deconv'):
            lab = plotlabels.deconv
            dgroup.plot_yarrays = [('deconv', PLOTOPTS_1, lab)]

        elif pchoice == 'deconv+norm' and hasattr(dgroup, 'deconv'):
            lab1 = plotlabels.norm
            lab2 = plotlabels.deconv
            dgroup.plot_yarrays = [('deconv', PLOTOPTS_1, lab2),
                                   ('norm', PLOTOPTS_1, lab1)]
            lab = lab1 + ' + ' + lab2

        dgroup.plot_ylabel = lab
        y4e0 = dgroup.ydat = getattr(dgroup, dgroup.plot_yarrays[0][0],
                                     dgroup.mu)
        dgroup.plot_extras = []
        if form['show_e0']:
            ie0 = index_of(dgroup.energy, dgroup.e0)
            dgroup.plot_extras.append(('marker', dgroup.e0, y4e0[ie0], {}))

    def plot(self,
             dgroup,
             title=None,
             plot_yarrays=None,
             delay_draw=False,
             new=True,
             zoom_out=True,
             with_extras=True,
             **kws):

        self.get_plot_arrays(dgroup)
        ppanel = self.controller.get_display(stacked=False).panel
        viewlims = ppanel.get_viewlimits()
        plotcmd = ppanel.oplot
        if new:
            plotcmd = ppanel.plot

        groupname = dgroup.groupname

        if not hasattr(dgroup, 'xdat'):
            print("Cannot plot group ", groupname)

        if ((getattr(dgroup, 'plot_yarrays', None) is None
             or getattr(dgroup, 'energy', None) is None
             or getattr(dgroup, 'mu', None) is None)):
            self.process(dgroup)

        if plot_yarrays is None and hasattr(dgroup, 'plot_yarrays'):
            plot_yarrays = dgroup.plot_yarrays

        popts = kws
        path, fname = os.path.split(dgroup.filename)
        if not 'label' in popts:
            popts['label'] = dgroup.plot_ylabel
        zoom_out = (zoom_out or min(dgroup.xdat) >= viewlims[1]
                    or max(dgroup.xdat) <= viewlims[0]
                    or min(dgroup.ydat) >= viewlims[3]
                    or max(dgroup.ydat) <= viewlims[2])

        if not zoom_out:
            popts['xmin'] = viewlims[0]
            popts['xmax'] = viewlims[1]
            popts['ymin'] = viewlims[2]
            popts['ymax'] = viewlims[3]

        popts['xlabel'] = dgroup.plot_xlabel
        popts['ylabel'] = dgroup.plot_ylabel
        if getattr(dgroup, 'plot_y2label', None) is not None:
            popts['y2label'] = dgroup.plot_y2label

        plot_extras = None
        if new:
            if title is None:
                title = fname
            plot_extras = getattr(dgroup, 'plot_extras', None)

        popts['title'] = title
        popts['delay_draw'] = delay_draw
        if hasattr(dgroup, 'custom_plotopts'):
            popts.update(dgroup.custom_plotopts)

        narr = len(plot_yarrays) - 1
        for i, pydat in enumerate(plot_yarrays):
            yaname, yopts, yalabel = pydat
            popts.update(yopts)
            if yalabel is not None:
                popts['label'] = yalabel

            popts['delay_draw'] = delay_draw or (i != narr)
            # print("plot:: ", i, popts['delay_draw'], plotcmd)
            # print(popts)
            plotcmd(dgroup.xdat, getattr(dgroup, yaname), **popts)
            plotcmd = ppanel.oplot

        if with_extras and plot_extras is not None:
            axes = ppanel.axes
            for etype, x, y, opts in plot_extras:
                if etype == 'marker':
                    xpopts = {
                        'marker': 'o',
                        'markersize': 4,
                        'label': '_nolegend_',
                        'markerfacecolor': 'red',
                        'markeredgecolor': '#884444'
                    }
                    xpopts.update(opts)
                    axes.plot([x], [y], **xpopts)
                elif etype == 'vline':
                    xpopts = {
                        'ymin': 0,
                        'ymax': 1.0,
                        'label': '_nolegend_',
                        'color': '#888888'
                    }
                    xpopts.update(opts)
                    axes.axvline(x, **xpopts)
        if not popts['delay_draw']:
            ppanel.canvas.draw()
Esempio n. 5
0
class ColumnDataFileFrame(wx.Frame):
    """Column Data File, select columns"""
    def __init__(self,
                 parent,
                 filename=None,
                 groupname=None,
                 last_array_sel=None,
                 read_ok_cb=None,
                 edit_groupname=True,
                 _larch=None):
        self.parent = parent
        self._larch = _larch
        self.path = filename

        group = self.initgroup = self.read_column_file(self.path)
        self.subframes = {}
        self.workgroup = Group(raw=group)
        for attr in ('path', 'filename', 'groupname', 'datatype',
                     'array_labels'):
            setattr(self.workgroup, attr, getattr(group, attr, None))

        arr_labels = [l.lower() for l in self.initgroup.array_labels]

        if self.workgroup.datatype is None:
            self.workgroup.datatype = 'raw'
            if ('energ' in arr_labels[0] or 'energ' in arr_labels[1]):
                self.workgroup.datatype = 'xas'

        self.read_ok_cb = read_ok_cb
        self.array_sel = {
            'xpop': '',
            'xarr': None,
            'ypop': '',
            'yop': '/',
            'yarr1': None,
            'yarr2': None,
            'use_deriv': False
        }

        if last_array_sel is not None:
            self.array_sel.update(last_array_sel)

        if self.array_sel['yarr2'] is None and 'i0' in arr_labels:
            self.array_sel['yarr2'] = 'i0'

        if self.array_sel['yarr1'] is None:
            if 'itrans' in arr_labels:
                self.array_sel['yarr1'] = 'itrans'
            elif 'i1' in arr_labels:
                self.array_sel['yarr1'] = 'i1'
        message = "Data Columns for %s" % group.filename
        wx.Frame.__init__(self,
                          None,
                          -1,
                          'Build Arrays from Data Columns for %s' %
                          group.filename,
                          style=FRAMESTYLE)

        self.SetFont(Font(10))

        panel = wx.Panel(self)
        self.SetMinSize((600, 600))
        self.colors = GUIColors()

        # title row
        title = SimpleText(panel,
                           message,
                           font=Font(13),
                           colour=self.colors.title,
                           style=LCEN)

        opts = dict(action=self.onUpdate, size=(120, -1))
        yarr_labels = self.yarr_labels = arr_labels + ['1.0', '0.0', '']
        xarr_labels = self.xarr_labels = arr_labels + ['_index']

        self.xarr = Choice(panel, choices=xarr_labels, **opts)
        self.yarr1 = Choice(panel, choices=arr_labels, **opts)
        self.yarr2 = Choice(panel, choices=yarr_labels, **opts)
        self.yerr_arr = Choice(panel, choices=yarr_labels, **opts)
        self.yerr_arr.Disable()

        self.datatype = Choice(panel, choices=DATATYPES, **opts)
        self.datatype.SetStringSelection(self.workgroup.datatype)

        opts['size'] = (50, -1)
        self.yop = Choice(panel, choices=ARR_OPS, **opts)

        opts['size'] = (120, -1)

        self.use_deriv = Check(panel,
                               label='use derivative',
                               default=self.array_sel['use_deriv'],
                               **opts)

        self.xpop = Choice(panel, choices=XPRE_OPS, **opts)
        self.ypop = Choice(panel, choices=YPRE_OPS, **opts)

        opts['action'] = self.onYerrChoice
        self.yerr_op = Choice(panel, choices=YERR_OPS, **opts)
        self.yerr_op.SetSelection(0)

        self.yerr_const = FloatCtrl(panel, value=1, precision=4, size=(90, -1))

        ylab = SimpleText(panel, 'Y = ')
        xlab = SimpleText(panel, 'X = ')
        yerr_lab = SimpleText(panel, 'Yerror = ')
        self.xsuf = SimpleText(panel, '')
        self.ysuf = SimpleText(panel, '')

        self.xpop.SetStringSelection(self.array_sel['xpop'])
        self.ypop.SetStringSelection(self.array_sel['ypop'])
        self.yop.SetStringSelection(self.array_sel['yop'])
        if '(' in self.array_sel['ypop']:
            self.ysuf.SetLabel(')')

        ixsel, iysel, iy2sel = 0, 1, len(yarr_labels) - 1
        if self.array_sel['xarr'] in xarr_labels:
            ixsel = xarr_labels.index(self.array_sel['xarr'])
        if self.array_sel['yarr1'] in arr_labels:
            iysel = arr_labels.index(self.array_sel['yarr1'])
        if self.array_sel['yarr2'] in yarr_labels:
            iy2sel = yarr_labels.index(self.array_sel['yarr2'])
        self.xarr.SetSelection(ixsel)
        self.yarr1.SetSelection(iysel)
        self.yarr2.SetSelection(iy2sel)

        bpanel = wx.Panel(panel)
        bsizer = wx.BoxSizer(wx.HORIZONTAL)
        _ok = Button(bpanel, 'OK', action=self.onOK)
        _cancel = Button(bpanel, 'Cancel', action=self.onCancel)
        _edit = Button(bpanel, 'Edit Array Names', action=self.onEditNames)
        bsizer.Add(_ok)
        bsizer.Add(_cancel)
        bsizer.Add(_edit)
        _ok.SetDefault()
        pack(bpanel, bsizer)

        sizer = wx.GridBagSizer(4, 8)
        sizer.Add(title, (0, 0), (1, 7), LCEN, 5)

        ir = 1
        sizer.Add(xlab, (ir, 0), (1, 1), LCEN, 0)
        sizer.Add(self.xpop, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(self.xarr, (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.xsuf, (ir, 3), (1, 1), CEN, 0)

        ir += 1
        sizer.Add(ylab, (ir, 0), (1, 1), LCEN, 0)
        sizer.Add(self.ypop, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(self.yarr1, (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.yop, (ir, 3), (1, 1), CEN, 0)
        sizer.Add(self.yarr2, (ir, 4), (1, 1), CEN, 0)
        sizer.Add(self.ysuf, (ir, 5), (1, 1), CEN, 0)
        sizer.Add(self.use_deriv, (ir, 6), (1, 1), LCEN, 0)

        ir += 1
        sizer.Add(yerr_lab, (ir, 0), (1, 1), LCEN, 0)
        sizer.Add(self.yerr_op, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(self.yerr_arr, (ir, 2), (1, 1), CEN, 0)
        sizer.Add(SimpleText(panel, 'Value:'), (ir, 3), (1, 1), CEN, 0)
        sizer.Add(self.yerr_const, (ir, 4), (1, 2), CEN, 0)

        ir += 1
        sizer.Add(SimpleText(panel, 'Data Type:'), (ir, 0), (1, 1), LCEN, 0)
        sizer.Add(self.datatype, (ir, 1), (1, 2), LCEN, 0)

        ir += 1
        self.wid_groupname = wx.TextCtrl(panel,
                                         value=group.groupname,
                                         size=(240, -1))
        if not edit_groupname:
            self.wid_groupname.Disable()

        sizer.Add(SimpleText(panel, 'Group Name:'), (ir, 0), (1, 1), LCEN, 0)
        sizer.Add(self.wid_groupname, (ir, 1), (1, 2), LCEN, 0)

        ir += 1
        sizer.Add(bpanel, (ir, 0), (1, 5), LCEN, 3)

        pack(panel, sizer)

        self.nb = fnb.FlatNotebook(self, -1, agwStyle=FNB_STYLE)
        self.nb.SetTabAreaColour(wx.Colour(248, 248, 240))
        self.nb.SetActiveTabColour(wx.Colour(254, 254, 195))
        self.nb.SetNonActiveTabTextColour(wx.Colour(40, 40, 180))
        self.nb.SetActiveTabTextColour(wx.Colour(80, 0, 0))

        self.plotpanel = PlotPanel(self, messenger=self.plot_messages)
        textpanel = wx.Panel(self)
        ftext = wx.TextCtrl(textpanel,
                            style=wx.TE_MULTILINE | wx.TE_READONLY,
                            size=(400, 250))

        ftext.SetValue(group.text)
        ftext.SetFont(Font(10))

        textsizer = wx.BoxSizer(wx.VERTICAL)
        textsizer.Add(ftext, 1, LCEN | wx.GROW, 1)
        pack(textpanel, textsizer)

        self.nb.AddPage(textpanel, ' Text of Data File ', True)
        self.nb.AddPage(self.plotpanel, ' Plot of Selected Arrays ', True)

        mainsizer = wx.BoxSizer(wx.VERTICAL)
        mainsizer.Add(panel, 0, wx.GROW | wx.ALL, 2)
        mainsizer.Add(self.nb, 1, LCEN | wx.GROW, 2)
        pack(self, mainsizer)

        self.statusbar = self.CreateStatusBar(2, 0)
        self.statusbar.SetStatusWidths([-1, -1])
        statusbar_fields = [group.filename, ""]
        for i in range(len(statusbar_fields)):
            self.statusbar.SetStatusText(statusbar_fields[i], i)

        self.Show()
        self.Raise()
        self.onUpdate(self)

    def read_column_file(self, path):
        """read column file, generally as initial read"""
        parent, filename = os.path.split(path)
        with open(path, 'r') as fh:
            lines = fh.readlines()

        text = ''.join(lines)
        line1 = lines[0].lower()

        reader = 'read_ascii'
        if 'epics stepscan file' in line1:
            reader = 'read_gsexdi'
        elif 'xdi' in line1:
            reader = 'read_xdi'
        elif 'epics scan' in line1:
            reader = 'read_gsescan'

        tmpname = '_tmp_file_'
        read_cmd = "%s = %s('%s')" % (tmpname, reader, path)
        self.reader = reader
        deeplarch = self._larch._larch
        try:
            deeplarch.eval(read_cmd, add_history=False)
        except:
            pass
        if deeplarch.error:
            # self._larch.input.clear()
            msg = ["Error trying to read '%s':" % path, ""]
            for err in deeplarch.error:
                exc_name, errmsg = err.get_error()
                msg.append(errmsg)

            title = "Cannot read %s" % path
            r = Popup(self.parent, "\n".join(msg), title)
            return None

        group = self._larch.symtable.get_symbol(tmpname)
        self._larch.symtable.del_symbol(tmpname)

        group.text = text
        group.path = path
        group.filename = filename
        group.groupname = file2groupname(filename,
                                         symtable=self._larch.symtable)
        return group

    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 onEditNames(self, evt=None):
        self.show_subframe('editcol',
                           EditColumnFrame,
                           group=self.workgroup,
                           on_ok=self.set_array_labels)

    def set_array_labels(self, arr_labels):
        self.workgroup.array_labels = arr_labels
        yarr_labels = self.yarr_labels = arr_labels + ['1.0', '0.0', '']
        xarr_labels = self.xarr_labels = arr_labels + ['_index']

        def update(wid, choices):
            curstr = wid.GetStringSelection()
            curind = wid.GetSelection()
            wid.SetChoices(choices)
            if curstr in choices:
                wid.SetStringSelection(curstr)
            else:
                wid.SetSelection(curind)

        update(self.xarr, xarr_labels)
        update(self.yarr1, yarr_labels)
        update(self.yarr2, yarr_labels)
        update(self.yerr_arr, yarr_labels)
        self.onUpdate()

    def onOK(self, event=None):
        """ build arrays according to selection """
        if self.wid_groupname is not None:
            groupname = fix_varname(self.wid_groupname.GetValue())

        yerr_op = self.yerr_op.GetStringSelection().lower()
        yerr_expr = '1'
        if yerr_op.startswith('const'):
            yerr_expr = "%f" % self.yerr_const.GetValue()
        elif yerr_op.startswith('array'):
            yerr_expr = '%%s.data[%i, :]' % self.yerr_arr.GetSelection()
        elif yerr_op.startswith('sqrt'):
            yerr_expr = 'sqrt(%s.ydat)'
        self.expressions['yerr'] = yerr_expr

        # generate script to pass back to calling program:
        labels = ', '.join(self.workgroup.array_labels)
        read_cmd = "%s('{path:s}', labels='%s')" % (self.reader, labels)

        buff = ["{group:s} = %s" % read_cmd, "{group:s}.path = '{path:s}'"]

        for attr in ('datatype', 'plot_xlabel', 'plot_ylabel'):
            val = getattr(self.workgroup, attr)
            buff.append("{group:s}.%s = '%s'" % (attr, val))

        for aname in ('xdat', 'ydat', 'yerr'):
            expr = self.expressions[aname].replace('%s', '{group:s}')
            buff.append("{group:s}.%s = %s" % (aname, expr))

        if getattr(self.workgroup, 'datatype', 'raw') == 'xas':
            if self.reader == 'read_gsescan':
                buff.append("{group:s}.energy = {group:s}.x")
            else:
                buff.append("{group:s}.energy = {group:s}.xdat")
            buff.append("{group:s}.mu = {group:s}.ydat")

        script = "\n".join(buff)

        if self.read_ok_cb is not None:
            self.read_ok_cb(script,
                            self.path,
                            groupname=groupname,
                            array_sel=self.array_sel)

        self.Destroy()

    def onCancel(self, event=None):
        self.workgroup.import_ok = False
        self.Destroy()

    def onYerrChoice(self, evt=None):
        yerr_choice = evt.GetString()
        self.yerr_arr.Disable()
        self.yerr_const.Disable()
        if 'const' in yerr_choice.lower():
            self.yerr_const.Enable()
        elif 'array' in yerr_choice.lower():
            self.yerr_arr.Enable()
        self.onUpdate()

    def onUpdate(self, value=None, evt=None):
        """column selections changed calc xdat and ydat"""
        # dtcorr = self.dtcorr.IsChecked()
        # print("Column Frame on Update ")

        dtcorr = False
        use_deriv = self.use_deriv.IsChecked()
        rawgroup = self.initgroup
        workgroup = self.workgroup
        rdata = self.initgroup.data

        # print("onUpdate ", dir(rawgroup))
        ix = self.xarr.GetSelection()
        xname = self.xarr.GetStringSelection()

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

        ncol, npts = rdata.shape
        if xname.startswith('_index') or ix >= ncol:
            workgroup.xdat = 1.0 * np.arange(npts)
            xname = '_index'
            exprs['xdat'] = 'arange(%i)' % npts
        else:
            workgroup.xdat = rdata[ix, :]
            exprs['xdat'] = '%%s.data[%i, : ]' % ix

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

        def pre_op(opwid, arr):
            opstr = opwid.GetStringSelection().strip()
            suf = ''
            if opstr in ('-log(', 'log('):
                suf = ')'
                if opstr == 'log(':
                    arr = np.log(arr)
                elif opstr == '-log(':
                    arr = -np.log(arr)
            return suf, opstr, arr

        try:
            xsuf, xpop, workgroup.xdat = pre_op(self.xpop, workgroup.xdat)
            self.xsuf.SetLabel(xsuf)
            exprs['xdat'] = '%s%s%s' % (xpop, exprs['xdat'], xsuf)
        except:
            return
        try:
            xunits = rawgroup.array_units[ix].strip()
            xlabel = '%s (%s)' % (xname, xunits)
        except:
            xlabel = xname

        yname1 = self.yarr1.GetStringSelection().strip()
        yname2 = self.yarr2.GetStringSelection().strip()
        iy1 = self.yarr1.GetSelection()
        iy2 = self.yarr2.GetSelection()
        yop = self.yop.GetStringSelection().strip()

        ylabel = yname1
        if len(yname2) == 0:
            yname2 = '1.0'
        else:
            ylabel = "%s%s%s" % (ylabel, yop, yname2)

        if yname1 == '0.0':
            yarr1 = np.zeros(npts) * 1.0
            yexpr1 = 'zeros(%i)' % npts
        elif len(yname1) == 0 or yname1 == '1.0' or iy1 >= ncol:
            yarr1 = np.ones(npts) * 1.0
            yexpr1 = 'ones(%i)' % npts
        else:
            yarr1 = rdata[iy1, :]
            yexpr1 = '%%s.data[%i, : ]' % iy1

        if yname2 == '0.0':
            yarr2 = np.zeros(npts) * 1.0
            yexpr2 = '0.0'
        elif len(yname2) == 0 or yname2 == '1.0' or iy2 >= ncol:
            yarr2 = np.ones(npts) * 1.0
            yexpr2 = '1.0'
        else:
            yarr2 = rdata[iy2, :]
            yexpr2 = '%%s.data[%i, : ]' % iy2

        workgroup.ydat = yarr1
        exprs['ydat'] = yexpr1
        if yop in ('+', '-', '*', '/'):
            exprs['ydat'] = "%s %s %s" % (yexpr1, yop, yexpr2)
            if yop == '+':
                workgroup.ydat = yarr1.__add__(yarr2)
            elif yop == '-':
                workgroup.ydat = yarr1.__sub__(yarr2)
            elif yop == '*':
                workgroup.ydat = yarr1.__mul__(yarr2)
            elif yop == '/':
                workgroup.ydat = yarr1.__truediv__(yarr2)

        ysuf, ypop, workgroup.ydat = pre_op(self.ypop, workgroup.ydat)
        self.ysuf.SetLabel(ysuf)
        exprs['ydat'] = '%s%s%s' % (ypop, exprs['ydat'], ysuf)

        yerr_op = self.yerr_op.GetStringSelection().lower()
        exprs['yerr'] = '1'
        if yerr_op.startswith('const'):
            yerr = self.yerr_const.GetValue()
            exprs['yerr'] = '%f' % yerr
        elif yerr_op.startswith('array'):
            iyerr = self.yerr_arr.GetSelection()
            yerr = rdata[iyerr, :]
            exprs['yerr'] = '%%s.data[%i, :]' % iyerr
        elif yerr_op.startswith('sqrt'):
            yerr = np.sqrt(workgroup.ydat)
            exprs['yerr'] = 'sqrt(%s.ydat)'

        if use_deriv:
            try:
                workgroup.ydat = (np.gradient(workgroup.ydat) /
                                  np.gradient(workgroup.xdat))
                exprs['ydat'] = 'deriv(%s)/deriv(%s)' % (exprs['ydat'],
                                                         exprs['xdat'])
            except:
                pass

        self.expressions = exprs
        self.array_sel = {
            'xpop': xpop,
            'xarr': xname,
            'ypop': ypop,
            'yop': yop,
            'yarr1': yname1,
            'yarr2': yname2,
            'use_deriv': use_deriv
        }

        try:
            npts = min(len(workgroup.xdat), len(workgroup.ydat))
        except AttributeError:
            return

        workgroup.filename = rawgroup.filename
        workgroup.npts = npts
        workgroup.plot_xlabel = xlabel
        workgroup.plot_ylabel = ylabel
        workgroup.xdat = np.array(workgroup.xdat[:npts])
        workgroup.ydat = np.array(workgroup.ydat[:npts])
        workgroup.y = workgroup.ydat
        workgroup.yerr = yerr
        if isinstance(yerr, np.ndarray):
            workgroup.yerr = np.array(yerr[:npts])

        if workgroup.datatype == 'xas':
            workgroup.energy = workgroup.xdat
            workgroup.mu = workgroup.ydat

        path, fname = os.path.split(workgroup.filename)
        popts = dict(marker='o',
                     markersize=4,
                     linewidth=1.5,
                     title=fname,
                     ylabel=ylabel,
                     xlabel=xlabel,
                     label="%s: %s" % (fname, workgroup.plot_ylabel))

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

        for i in range(self.nb.GetPageCount()):
            if 'plot' in self.nb.GetPageText(i).lower():
                self.nb.SetSelection(i)

    def plot_messages(self, msg, panel=1):
        self.SetStatusText(msg, panel)
Esempio n. 6
0
class EditColumnFrame(wx.Frame):
    """Set Column Labels for a file"""
    def __init__(self,
                 parent,
                 group=None,
                 last_array_sel=None,
                 read_ok_cb=None,
                 edit_groupname=True):
        self.parent = parent
        self.dgroup = group
        if not hasattr(self.dgroup, 'is_xas'):
            try:
                self.dgroup.is_xas = 'energ' in self.dgroup.array_labels[
                    0].lower()
            except:
                self.dgroup.is_xas = False
        self.read_ok_cb = read_ok_cb

        self.array_sel = {
            'xpop': '',
            'xarr': None,
            'ypop': '',
            'yop': '/',
            'yarr1': None,
            'yarr2': None,
            'use_deriv': False
        }
        if last_array_sel is not None:
            self.array_sel.update(last_array_sel)

        if self.array_sel['yarr2'] is None and 'i0' in self.dgroup.array_labels:
            self.array_sel['yarr2'] = 'i0'

        if self.array_sel['yarr1'] is None:
            if 'itrans' in self.dgroup.array_labels:
                self.array_sel['yarr1'] = 'itrans'
            elif 'i1' in self.dgroup.array_labels:
                self.array_sel['yarr1'] = 'i1'
        message = "Build Arrys from Data Columns for %s" % self.dgroup.filename
        wx.Frame.__init__(self,
                          None,
                          -1,
                          'Build Arrays from Data Columns for %s' %
                          self.dgroup.filename,
                          style=FRAMESTYLE)

        self.SetFont(Font(10))
        panel = scrolled.ScrolledPanel(self)
        self.SetMinSize((600, 600))
        self.colors = GUIColors()
        self.plotframe = None

        # title row
        title = SimpleText(panel,
                           message,
                           font=Font(13),
                           colour=self.colors.title,
                           style=LCEN)

        opts = dict(action=self.onColumnChoice, size=(120, -1))

        arr_labels = self.dgroup.array_labels
        yarr_labels = arr_labels + ['1.0', '0.0', '']
        xarr_labels = arr_labels + ['<index>']

        self.xarr = Choice(panel, choices=xarr_labels, **opts)
        self.yarr1 = Choice(panel, choices=arr_labels, **opts)
        self.yarr2 = Choice(panel, choices=yarr_labels, **opts)

        opts['size'] = (90, -1)

        self.xpop = Choice(panel, choices=XPRE_OPS, **opts)
        self.ypop = Choice(panel, choices=YPRE_OPS, **opts)
        opts['size'] = (50, -1)
        self.yop = Choice(panel, choices=ARR_OPS, **opts)

        ylab = SimpleText(panel, 'Y = ')
        xlab = SimpleText(panel, 'X = ')
        self.xsuf = SimpleText(panel, '')
        self.ysuf = SimpleText(panel, '')

        self.xpop.SetStringSelection(self.array_sel['xpop'])
        self.ypop.SetStringSelection(self.array_sel['ypop'])
        self.yop.SetStringSelection(self.array_sel['yop'])
        if '(' in self.array_sel['ypop']:
            self.ysuf.SetLabel(')')

        ixsel, iysel, iy2sel = 0, 1, len(yarr_labels) - 1
        if self.array_sel['xarr'] in xarr_labels:
            ixsel = xarr_labels.index(self.array_sel['xarr'])
        if self.array_sel['yarr1'] in arr_labels:
            iysel = arr_labels.index(self.array_sel['yarr1'])
        if self.array_sel['yarr2'] in yarr_labels:
            iy2sel = yarr_labels.index(self.array_sel['yarr2'])
        self.xarr.SetSelection(ixsel)
        self.yarr1.SetSelection(iysel)
        self.yarr2.SetSelection(iy2sel)

        opts['size'] = (150, -1)
        self.use_deriv = Check(panel,
                               label='use derivative',
                               default=self.array_sel['use_deriv'],
                               **opts)

        self.is_xas = Check(panel,
                            label='use as XAS data',
                            default=self.dgroup.is_xas,
                            **opts)

        bpanel = wx.Panel(panel)
        bsizer = wx.BoxSizer(wx.HORIZONTAL)
        bsizer.Add(Button(bpanel, 'Preview', action=self.onColumnChoice), 4)
        bsizer.Add(Button(bpanel, 'OK', action=self.onOK), 4)
        bsizer.Add(Button(bpanel, 'Cancel', action=self.onCancel), 4)
        pack(bpanel, bsizer)

        sizer = wx.GridBagSizer(4, 8)
        sizer.Add(title, (0, 0), (1, 7), LCEN, 5)

        ir = 1
        sizer.Add(xlab, (ir, 0), (1, 1), CEN, 0)
        sizer.Add(self.xpop, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(self.xarr, (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.xsuf, (ir, 3), (1, 1), CEN, 0)

        ir += 1
        sizer.Add(ylab, (ir, 0), (1, 1), CEN, 0)
        sizer.Add(self.ypop, (ir, 1), (1, 1), CEN, 0)
        sizer.Add(self.yarr1, (ir, 2), (1, 1), CEN, 0)
        sizer.Add(self.yop, (ir, 3), (1, 1), CEN, 0)
        sizer.Add(self.yarr2, (ir, 4), (1, 1), CEN, 0)
        sizer.Add(self.ysuf, (ir, 5), (1, 1), CEN, 0)

        ir += 1
        sizer.Add(self.use_deriv, (ir, 0), (1, 3), LCEN, 0)
        ir += 1
        sizer.Add(self.is_xas, (ir, 0), (1, 3), LCEN, 0)

        self.wid_groupname = None
        if edit_groupname:
            wid_grouplab = SimpleText(panel, 'Use Group Name: ')
            self.wid_groupname = wx.TextCtrl(panel,
                                             value=self.dgroup._groupname,
                                             size=(100, -1))
            ir += 1
            sizer.Add(wid_grouplab, (ir, 0), (1, 2), LCEN, 3)
            sizer.Add(self.wid_groupname, (ir, 2), (1, 3), LCEN, 3)

        ir += 1
        sizer.Add(bpanel, (ir, 0), (1, 5), LCEN, 3)

        pack(panel, sizer)

        ftext = wx.TextCtrl(self,
                            style=wx.TE_MULTILINE | wx.TE_READONLY,
                            size=(-1, 150))
        try:
            m = open(self.dgroup.filename, 'r')
            text = m.read()
            m.close()
        except:
            text = "The file '%s'\n was not found" % self.dgroup.filename
        ftext.SetValue(text)
        ftext.SetFont(Font(9))

        mainsizer = wx.BoxSizer(wx.VERTICAL)
        mainsizer.Add(panel, 0, wx.GROW | wx.ALL, 2)
        mainsizer.Add(ftext, 1, LCEN | wx.GROW, 2)
        pack(self, mainsizer)

        self.Show()
        self.Raise()

    def onOK(self, event=None):
        """ build arrays according to selection """

        if not hasattr(self.dgroup, '_xdat'):
            self.onColumnChoice()

        if self.wid_groupname is not None:
            self.dgroup._groupname = fix_varname(self.wid_groupname.GetValue())
        if self.plotframe is not None:
            try:
                self.plotframe.Destroy()
            except:
                pass
        if self.read_ok_cb is not None:
            self.read_ok_cb(self.dgroup, self.array_sel)
        self.Destroy()

    def onCancel(self, event=None):
        self.dgroup.import_ok = False
        if self.plotframe is not None:
            self.plotframe.Destroy()
        self.Destroy()

    def onColumnChoice(self, evt=None):
        """column selections changed calc _xdat and _ydat"""
        # dtcorr = self.dtcorr.IsChecked()
        dtcorr = False
        use_deriv = self.use_deriv.IsChecked()
        dgroup = self.dgroup

        ix = self.xarr.GetSelection()
        xname = self.xarr.GetStringSelection()
        if xname == '<index>':
            xname = self.dgroup.array_labels[0]
            dgroup._index = 1.0 * np.arange(len(getattr(dgroup, xname)))
            xname = '_index'

        dgroup.is_xas = self.is_xas.IsChecked()
        dgroup._xdat = getattr(dgroup, xname)

        def do_preop(opwid, arr):
            opstr = opwid.GetStringSelection().strip()
            suf = ''
            if opstr in ('-log(', 'log('):
                suf = ')'
                if opstr == 'log(':
                    arr = np.log(arr)
                elif opstr == '-log(':
                    arr = -np.log(arr)
            return suf, opstr, arr

        xsuf, xpop, dgroup._xdat = do_preop(self.xpop, dgroup._xdat)
        self.xsuf.SetLabel(xsuf)

        try:
            xunits = dgroup.array_units[ix].strip()
            xlabel = '%s (%s)' % (xname, xunits)
        except:
            xlabel = xname

        def get_data(group, arrayname, correct=False):
            if hasattr(group, 'get_data'):
                return group.get_data(arrayname, correct=correct)
            return getattr(group, arrayname, None)

        yname1 = self.yarr1.GetStringSelection().strip()
        yname2 = self.yarr2.GetStringSelection().strip()
        yop = self.yop.GetStringSelection().strip()

        ylabel = yname1
        if len(yname2) == 0:
            yname2, yop = '1.0', '*'
        else:
            ylabel = "%s%s%s" % (ylabel, yop, yname2)

        yarr1 = get_data(dgroup, yname1, correct=dtcorr)

        if yname2 in ('0.0', '1.0'):
            yarr2 = float(yname2)
            if yop == '/': yarr2 = 1.0
        else:
            yarr2 = get_data(dgroup, yname2, correct=dtcorr)

        dgroup._ydat = yarr1
        if yop == '+':
            dgroup._ydat = yarr1.__add__(yarr2)
        elif yop == '-':
            dgroup._ydat = yarr1.__sub__(yarr2)
        elif yop == '*':
            dgroup._ydat = yarr1.__mul__(yarr2)
        elif yop == '/':
            dgroup._ydat = yarr1.__truediv__(yarr2)

        ysuf, ypop, dgroup._ydat = do_preop(self.ypop, dgroup._ydat)
        self.ysuf.SetLabel(ysuf)

        if use_deriv:
            try:
                dgroup._ydat = np.gradient(dgroup._ydat) / np.gradient(
                    dgroup._xdat)
            except:
                pass

        self.array_sel = {
            'xpop': xpop,
            'xarr': xname,
            'ypop': ypop,
            'yop': yop,
            'yarr1': yname1,
            'yarr2': yname2,
            'use_deriv': use_deriv
        }

        try:
            npts = min(len(dgroup._xdat), len(dgroup._ydat))
        except AttributeError:
            print('Error calculating arrays (npts not correct)')
            return

        dgroup._npts = npts
        dgroup.plot_xlabel = xlabel
        dgroup.plot_ylabel = ylabel
        dgroup._xdat = np.array(dgroup._xdat[:npts])
        dgroup._ydat = np.array(dgroup._ydat[:npts])
        if dgroup.is_xas:
            dgroup.energy = dgroup._xdat
            dgroup.mu = dgroup._ydat

        self.raise_plotframe()

        ppanel = self.plotframe.panel

        popts = {}
        path, fname = os.path.split(dgroup.filename)

        popts['label'] = "%s: %s" % (fname, dgroup.plot_ylabel)
        popts['title'] = fname
        popts['ylabel'] = dgroup.plot_ylabel
        popts['xlabel'] = dgroup.plot_xlabel

        ppanel.plot(dgroup._xdat, dgroup._ydat, **popts)

    def raise_plotframe(self):
        if self.plotframe is not None:
            try:
                self.plotframe.Show()
            except:
                self.plotframe = None
        if self.plotframe is None:
            self.plotframe = PlotFrame(None, size=(650, 400))
            self.plotframe.Show()
Esempio n. 7
0
class PrePeakPanel(wx.Panel):
    def __init__(self, parent=None, controller=None, **kws):

        wx.Panel.__init__(self, parent, -1, size=(550, 625), **kws)
        self.parent = parent
        self.controller = controller
        self.larch = controller.larch
        self.fit_components = OrderedDict()
        self.fit_model = None
        self.fit_params = None
        self.user_added_params = None
        self.summary = None
        self.sizer = wx.GridBagSizer(10, 6)
        self.build_display()
        self.pick2_timer = wx.Timer(self)
        self.pick2_group = None
        self.Bind(wx.EVT_TIMER, self.onPick2Timer, self.pick2_timer)
        self.pick2_t0 = 0.
        self.pick2_timeout = 15.

        self.pick2erase_timer = wx.Timer(self)
        self.pick2erase_panel = None
        self.Bind(wx.EVT_TIMER, self.onPick2EraseTimer, self.pick2erase_timer)

    def onPanelExposed(self, **kws):
        # called when notebook is selected
        try:
            fname = self.controller.filelist.GetStringSelection()
            gname = self.controller.file_groups[fname]
            dgroup = self.controller.get_group(gname)
            # print(" Fill prepeak panel from group ", fname, gname, dgroup)
            self.fill_form_from_group(dgroup)
        except:
            pass # print(" Cannot Fill prepeak panel from group ")

    def larch_eval(self, cmd):
        """eval"""
        self.controller.larch.eval(cmd)

    def build_display(self):
        self.mod_nb = flat_nb.FlatNotebook(self, -1, agwStyle=FNB_STYLE)
        self.mod_nb.SetTabAreaColour(wx.Colour(250,250,250))
        self.mod_nb.SetActiveTabColour(wx.Colour(254,254,195))

        self.mod_nb.SetNonActiveTabTextColour(wx.Colour(10,10,128))
        self.mod_nb.SetActiveTabTextColour(wx.Colour(128,0,0))
        self.mod_nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onNBChanged)

        pan = self.panel = GridPanel(self, ncols=4, nrows=4, pad=2, itemstyle=LCEN)

        self.btns = {}
        for name in ('ppeak_e0', 'ppeak_elo', 'ppeak_emin', 'ppeak_emax', 'ppeak_ehi'):
            bb = BitmapButton(pan, get_icon('plus'),
                              action=partial(self.on_selpoint, opt=name),
                              tooltip='use last point selected from plot')
            self.btns[name] = bb

        opts = dict(size=(75, -1), gformat=True, precision=1)

        self.ppeak_e0   = FloatCtrl(pan, value=0, **opts)
        self.ppeak_emin = FloatCtrl(pan, value=-30, **opts)
        self.ppeak_emax = FloatCtrl(pan, value=0, **opts)
        self.ppeak_elo = FloatCtrl(pan, value=-15, **opts)
        self.ppeak_ehi = FloatCtrl(pan, value=-5, **opts)

        self.fitbline_btn  = Button(pan,'Fit Baseline', action=self.onFitBaseline,
                                    size=(150, 25))
        self.fitmodel_btn = Button(pan, 'Fit Model',
                                   action=self.onFitModel,  size=(150, 25))
        self.fitsel_btn = Button(pan, 'Fit Selected Groups',
                                 action=self.onFitSelected,  size=(150, 25))
        self.fitmodel_btn.Disable()
        self.fitsel_btn.Disable()

        self.array_choice = Choice(pan, size=(125, -1),
                                   choices=list(Array_Choices.keys()))
        self.array_choice.SetSelection(1)

        models_peaks = Choice(pan, size=(125, -1),
                              choices=ModelChoices['peaks'],
                              action=self.addModel)

        models_other = Choice(pan, size=(125, -1),
                              choices=ModelChoices['other'],
                              action=self.addModel)

        self.plot_choice = Choice(pan, size=(125, -1),
                                  choices=PlotChoices,
                                  action=self.onPlot)

        self.message = SimpleText(pan,
                                 'first fit baseline, then add peaks to fit model.')

        self.msg_centroid = SimpleText(pan, '----')

        opts = dict(default=True, size=(75, -1), action=self.onPlot)
        self.show_centroid  = Check(pan, label='show?', **opts)
        self.show_peakrange = Check(pan, label='show?', **opts)
        self.show_fitrange  = Check(pan, label='show?', **opts)
        self.show_e0        = Check(pan, label='show?', **opts)

        opts = dict(default=False, size=(200, -1), action=self.onPlot)
        self.plot_sub_bline = Check(pan, label='Subtract Baseline?', **opts)

        titleopts = dict(font=Font(11), colour='#AA0000')
        pan.Add(SimpleText(pan, ' Pre-edge Peak Fitting', **titleopts), dcol=7)
        pan.Add(SimpleText(pan, ' Run Fit:'), style=LCEN)

        pan.Add(SimpleText(pan, 'Array to fit: '), newrow=True)
        pan.Add(self.array_choice, dcol=4)
        pan.Add((10, 10), dcol=2)
        pan.Add(self.fitbline_btn)

        pan.Add(SimpleText(pan, 'E0: '), newrow=True)
        pan.Add(self.btns['ppeak_e0'])
        pan.Add(self.ppeak_e0)
        pan.Add((10, 10), dcol=3)
        pan.Add(self.show_e0)
        pan.Add(self.fitmodel_btn)


        pan.Add(SimpleText(pan, 'Fit Energy Range: '), newrow=True)
        pan.Add(self.btns['ppeak_emin'])
        pan.Add(self.ppeak_emin)
        pan.Add(SimpleText(pan, ':'))
        pan.Add(self.btns['ppeak_emax'])
        pan.Add(self.ppeak_emax)
        pan.Add(self.show_fitrange, dcol=1)
        pan.Add(self.fitsel_btn)



        t = SimpleText(pan, 'Pre-edge Peak Range: ')
        t.SetToolTip('Range used as mask for background')

        pan.Add(t, newrow=True)
        pan.Add(self.btns['ppeak_elo'])
        pan.Add(self.ppeak_elo)
        pan.Add(SimpleText(pan, ':'))
        pan.Add(self.btns['ppeak_ehi'])
        pan.Add(self.ppeak_ehi)
        pan.Add(self.show_peakrange, dcol=1)

        pan.Add(SimpleText(pan, 'Peak Centroid: '), newrow=True)
        pan.Add(self.msg_centroid, dcol=5)
        pan.Add(self.show_centroid, dcol=1)


        #  plot buttons
        ts = wx.BoxSizer(wx.HORIZONTAL)
        ts.Add(self.plot_choice)
        ts.Add(self.plot_sub_bline)

        pan.Add(SimpleText(pan, 'Plot: '), newrow=True)
        pan.Add(ts, dcol=7)

        #  add model
        ts = wx.BoxSizer(wx.HORIZONTAL)
        ts.Add(models_peaks)
        ts.Add(models_other)

        pan.Add(SimpleText(pan, 'Add Component: '), newrow=True)
        pan.Add(ts, dcol=7)

        pan.Add(SimpleText(pan, 'Messages: '), newrow=True)
        pan.Add(self.message, dcol=7)


        pan.pack()


        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add((5,5), 0, LCEN, 3)
        sizer.Add(HLine(self, size=(550, 2)), 0, LCEN, 3)
        sizer.Add(pan,   0, LCEN, 3)
        sizer.Add((5,5), 0, LCEN, 3)
        sizer.Add(HLine(self, size=(550, 2)), 0, LCEN, 3)
        sizer.Add((5,5), 0, LCEN, 3)
        sizer.Add(self.mod_nb,  1, LCEN|wx.GROW, 10)

        pack(self, sizer)

    def get_config(self, dgroup=None):
        """get processing configuration for a group"""
        if dgroup is None:
            dgroup = self.controller.get_group()

        conf = getattr(dgroup, 'prepeak_config', {})
        if 'e0' not in conf:
            conf = default_prepeak_config()
        if not hasattr(dgroup, 'prepeaks'):
            dgroup.prepeaks = Group()
        dgroup.prepeaks.config = conf
        return conf

    def fill_form_from_group(self, dgroup):
        self.ppeak_e0.SetValue(dgroup.e0)
        if hasattr(dgroup, 'prepeaks'):
            self.ppeak_emin.SetValue(dgroup.prepeaks.emin)
            self.ppeak_emax.SetValue(dgroup.prepeaks.emax)
            self.ppeak_elo.SetValue(dgroup.prepeaks.elo)
            self.ppeak_ehi.SetValue(dgroup.prepeaks.ehi)

    def fill_form_from_dict(self, data):
        self.ppeak_e0.SetValue(data['e0'])
        self.ppeak_emin.SetValue(data['emin'])
        self.ppeak_emax.SetValue(data['emax'])
        self.ppeak_elo.SetValue(data['elo'])
        self.ppeak_ehi.SetValue(data['ehi'])
        self.array_choice.SetStringSelection(data['array_desc'])
        self.show_e0.Enable(data['show_e0'])
        self.show_centroid.Enable(data['show_centroid'])
        self.show_fitrange.Enable(data['show_fitrange'])
        self.show_peakrange.Enable(data['show_peakrange'])
        self.plot_sub_bline.Enable(data['plot_sub_bline'])

    def read_form(self):
        "read for, returning dict of values"
        dgroup = self.controller.get_group()
        array_desc = self.array_choice.GetStringSelection()
        form_opts = {'gname': dgroup.groupname,
                     'array_desc': array_desc.lower(),
                     'array_name': Array_Choices[array_desc],
                     'baseline_form': 'lorentzian'}

        form_opts['e0'] = self.ppeak_e0.GetValue()
        form_opts['emin'] = self.ppeak_emin.GetValue()
        form_opts['emax'] = self.ppeak_emax.GetValue()
        form_opts['elo'] = self.ppeak_elo.GetValue()
        form_opts['ehi'] = self.ppeak_ehi.GetValue()
        form_opts['plot_sub_bline'] = self.plot_sub_bline.IsChecked()
        form_opts['show_centroid'] = self.show_centroid.IsChecked()
        form_opts['show_peakrange'] = self.show_peakrange.IsChecked()
        form_opts['show_fitrange'] = self.show_fitrange.IsChecked()
        form_opts['show_e0'] = self.show_e0.IsChecked()
        return form_opts


    def onFitBaseline(self, evt=None):
        opts = self.read_form()

        cmd = """{gname:s}.ydat = 1.0*{gname:s}.{array_name:s}
pre_edge_baseline(energy={gname:s}.energy, norm={gname:s}.ydat, group={gname:s},
form='{baseline_form:s}', with_line=True,
elo={elo:.3f}, ehi={ehi:.3f}, emin={emin:.3f}, emax={emax:.3f})
"""
        self.larch_eval(cmd.format(**opts))

        dgroup = self.controller.get_group()
        ppeaks = dgroup.prepeaks
        dgroup.centroid_msg = "%.4f +/- %.4f eV" % (ppeaks.centroid,
                                                    ppeaks.delta_centroid)

        self.msg_centroid.SetLabel(dgroup.centroid_msg)

        if 'loren_' not in self.fit_components:
            self.addModel(model='Lorentzian', prefix='loren_', isbkg=True)
        if 'line_' not in self.fit_components:
            self.addModel(model='Linear', prefix='line_', isbkg=True)

        for prefix in ('loren_', 'line_'):
            cmp = self.fit_components[prefix]
            # cmp.bkgbox.SetValue(1)
            self.fill_model_params(prefix, dgroup.prepeaks.fit_details.params)

        self.fill_form_from_group(dgroup)
        self.fitmodel_btn.Enable()
        # self.fitallmodel_btn.Enable()

        i1, i2 = self.get_xranges(dgroup.energy)
        dgroup.yfit = dgroup.xfit = 0.0*dgroup.energy[i1:i2]

        self.plot_choice.SetStringSelection(PLOT_BASELINE)
        self.onPlot()

    def fill_model_params(self, prefix, params):
        comp = self.fit_components[prefix]
        parwids = comp.parwids
        for pname, par in params.items():
            pname = prefix + pname
            if pname in parwids:
                wids = parwids[pname]
                if wids.minval is not None:
                    wids.minval.SetValue(par.min)
                if wids.maxval is not None:
                    wids.maxval.SetValue(par.max)
                wids.value.SetValue(par.value)
                varstr = 'vary' if par.vary else 'fix'
                if par.expr is not None:
                    varstr = 'constrain'
                if wids.vary is not None:
                    wids.vary.SetStringSelection(varstr)

    def onPlot(self, evt=None):
        plot_choice = self.plot_choice.GetStringSelection()

        opts = self.read_form()
        dgroup = self.controller.get_group()

        ppeaks = getattr(dgroup, 'prepeaks', None)
        if ppeaks is None:
            return

        i1, i2 = self.get_xranges(dgroup.xdat)
        # i2 = len(ppeaks.baseline) + i1

        if len(dgroup.yfit) > len(ppeaks.baseline):
            i2 = i1 + len(ppeaks.baseline)
        # print(" Indexes: ", i1, i2, i2-i1, len(dgroup.yfit), len(ppeaks.baseline))

        xdat = 1.0*dgroup.energy
        ydat = 1.0*dgroup.ydat
        yfit = 1.0*dgroup.ydat
        baseline = 1.0*dgroup.ydat
        yfit[i1:i2] = dgroup.yfit[:i2-i1]
        baseline[i1:i2] = ppeaks.baseline[:i2-i1]


        if opts['plot_sub_bline']:
            ydat = ydat - baseline
            if plot_choice in (PLOT_FIT, PLOT_RESID):
                yfit = yfit - baseline
        if plot_choice == PLOT_RESID:
            resid = ydat - yfit

        _xs = dgroup.energy[i1:i2]
        xrange = max(_xs) - min(_xs)
        pxmin = min(_xs) - 0.05 * xrange
        pxmax = max(_xs) + 0.05 * xrange

        jmin = index_of(dgroup.energy, pxmin)
        jmax = index_of(dgroup.energy, pxmax) + 1

        _ys = ydat[jmin:jmax]
        yrange = max(_ys) - min(_ys)
        pymin = min(_ys) - 0.05 * yrange
        pymax = max(_ys) + 0.05 * yrange

        title = ' pre-edge fit'
        if plot_choice == PLOT_BASELINE:
            title = ' pre-edge baseline'
            if opts['plot_sub_bline']:
                title = ' pre-edge peaks'

        array_desc = self.array_choice.GetStringSelection()

        plotopts = {'xmin': pxmin, 'xmax': pxmax,
                    'ymin': pymin, 'ymax': pymax,
                    'title': '%s: %s' % (opts['gname'], title),
                    'xlabel': 'Energy (eV)',
                    'ylabel': '%s $\mu$' % opts['array_desc'],
                    'label': '%s $\mu$' % opts['array_desc'],
                    'delay_draw': True,
                    'show_legend': True}

        plot_extras = []
        if opts['show_fitrange']:
            popts = {'color': '#DDDDCC'}
            emin = opts['emin']
            emax = opts['emax']
            imin = index_of(dgroup.energy, emin)
            imax = index_of(dgroup.energy, emax)

            plot_extras.append(('vline', emin, None, popts))
            plot_extras.append(('vline', emax, None, popts))

        if opts['show_peakrange']:
            popts = {'marker': '+', 'markersize': 6}
            elo = opts['elo']
            ehi = opts['ehi']
            ilo = index_of(dgroup.xdat, elo)
            ihi = index_of(dgroup.xdat, ehi)

            plot_extras.append(('marker', elo, ydat[ilo], popts))
            plot_extras.append(('marker', ehi, ydat[ihi], popts))

        if opts['show_centroid']:
            popts = {'color': '#EECCCC'}
            ecen = getattr(dgroup.prepeaks, 'centroid', -1)
            if ecen > min(dgroup.energy):
                plot_extras.append(('vline', ecen, None,  popts))


        pframe = self.controller.get_display(stacked=(plot_choice==PLOT_RESID))
        ppanel = pframe.panel
        axes = ppanel.axes

        plotopts.update(PLOTOPTS_1)

        ppanel.plot(xdat, ydat, **plotopts)
        if plot_choice == PLOT_BASELINE:
            if not opts['plot_sub_bline']:
                ppanel.oplot(dgroup.prepeaks.energy,
                             dgroup.prepeaks.baseline,
                             label='baseline', **PLOTOPTS_2)

        elif plot_choice in (PLOT_FIT, PLOT_RESID):
            ppanel.oplot(dgroup.energy, yfit,
                         label='fit', **PLOTOPTS_1)

            if hasattr(dgroup, 'ycomps'):
                ncomp = len(dgroup.ycomps)
                icomp = 0
                for label, ycomp in dgroup.ycomps.items():
                    icomp +=1
                    fcomp = self.fit_components[label]
                    # print("ycomp: ", plot_choice, label, len(ycomp), len(dgroup.xfit),
                    #       fcomp.bkgbox.IsChecked(), opts['plot_sub_bline'], icomp, ncomp)
                    if not (fcomp.bkgbox.IsChecked() and opts['plot_sub_bline']):
                        ppanel.oplot(dgroup.xfit, ycomp, label=label,
                                     delay_draw=(icomp!=ncomp), style='short dashed')

            if plot_choice == PLOT_RESID:
                _ys = resid
                yrange = max(_ys) - min(_ys)
                plotopts['ymin'] = min(_ys) - 0.05 * yrange
                plotopts['ymax'] = max(_ys) + 0.05 * yrange
                plotopts['delay_draw'] = False
                plotopts['ylabel'] = 'data-fit'
                plotopts['label'] = '_nolegend_'

                pframe.plot(dgroup.energy, resid, panel='bot', **plotopts)
                pframe.Show()
                # print(" RESIDUAL PLOT  margins: ")
                # print(" top : ", pframe.panel.conf.margins)
                # print(" bot : ", pframe.panel_bot.conf.margins)


        for etype, x, y, opts in plot_extras:
            if etype == 'marker':
                popts = {'marker': 'o', 'markersize': 4,
                         'label': '_nolegend_',
                         'markerfacecolor': 'red',
                         'markeredgecolor': '#884444'}
                popts.update(opts)
                axes.plot([x], [y], **popts)
            elif etype == 'vline':
                popts = {'ymin': 0, 'ymax': 1.0, 'color': '#888888',
                         'label': '_nolegend_'}
                popts.update(opts)
                axes.axvline(x, **popts)
        ppanel.canvas.draw()

    def onNBChanged(self, event=None):
        idx = self.mod_nb.GetSelection()

    def addModel(self, event=None, model=None, prefix=None, isbkg=False):
        if model is None and event is not None:
            model = event.GetString()
        if model is None or model.startswith('<'):
            return

        if prefix is None:
            p = model[:5].lower()
            curmodels = ["%s%i_" % (p, i+1) for i in range(1+len(self.fit_components))]
            for comp in self.fit_components:
                if comp in curmodels:
                    curmodels.remove(comp)

            prefix = curmodels[0]

        label = "%s(prefix='%s')" % (model, prefix)
        title = "%s: %s " % (prefix[:-1], model)
        title = prefix[:-1]
        mclass_kws = {'prefix': prefix}
        if 'step' in model.lower():
            form = model.lower().replace('step', '').strip()

            if form.startswith('err'): form = 'erf'
            label = "Step(form='%s', prefix='%s')" % (form, prefix)
            title = "%s: Step %s" % (prefix[:-1], form[:3])
            mclass = lm_models.StepModel
            mclass_kws['form'] = form
            minst = mclass(form=form, prefix=prefix)
        else:
            if model in ModelFuncs:
                mclass = getattr(lm_models, ModelFuncs[model])
            else:
                mclass = getattr(lm_models, model+'Model')

            minst = mclass(prefix=prefix)

        panel = GridPanel(self.mod_nb, ncols=1, nrows=1, pad=1, itemstyle=CEN)

        def SLabel(label, size=(80, -1), **kws):
            return  SimpleText(panel, label,
                               size=size, style=wx.ALIGN_LEFT, **kws)
        usebox = Check(panel, default=True, label='Use in Fit?', size=(100, -1))
        bkgbox = Check(panel, default=False, label='Is Baseline?', size=(125, -1))
        if isbkg:
            bkgbox.SetValue(1)

        delbtn = Button(panel, 'Delete Component', size=(125, -1),
                        action=partial(self.onDeleteComponent, prefix=prefix))

        pick2msg = SimpleText(panel, "    ", size=(125, -1))
        pick2btn = Button(panel, 'Pick Values from Data', size=(150, -1),
                          action=partial(self.onPick2Points, prefix=prefix))

        # SetTip(mname,  'Label for the model component')
        SetTip(usebox,   'Use this component in fit?')
        SetTip(bkgbox,   'Label this component as "background" when plotting?')
        SetTip(delbtn,   'Delete this model component')
        SetTip(pick2btn, 'Select X range on Plot to Guess Initial Values')

        panel.Add(SLabel(label, size=(275, -1), colour='#0000AA'),
                  dcol=3,  style=wx.ALIGN_LEFT, newrow=True)
        panel.Add(usebox, dcol=1)
        panel.Add(bkgbox, dcol=2, style=LCEN)
        panel.Add(delbtn, dcol=1, style=wx.ALIGN_LEFT)

        panel.Add(pick2btn, dcol=2, style=wx.ALIGN_LEFT, newrow=True)
        panel.Add(pick2msg, dcol=2, style=wx.ALIGN_RIGHT)

        # panel.Add((10, 10), newrow=True)
        # panel.Add(HLine(panel, size=(150,  3)), dcol=4, style=wx.ALIGN_CENTER)

        panel.Add(SLabel("Parameter "), style=wx.ALIGN_LEFT,  newrow=True)
        panel.AddMany((SLabel(" Value"), SLabel(" Type"), SLabel(' Bounds'),
                       SLabel("  Min", size=(60, -1)),
                       SLabel("  Max", size=(60, -1)),  SLabel(" Expression")))

        parwids = OrderedDict()
        parnames = sorted(minst.param_names)

        for a in minst._func_allargs:
            pname = "%s%s" % (prefix, a)
            if (pname not in parnames and
                a in minst.param_hints and
                a not in minst.independent_vars):
                parnames.append(pname)

        for pname in parnames:
            sname = pname[len(prefix):]
            hints = minst.param_hints.get(sname, {})

            par = Parameter(name=pname, value=0, vary=True)
            if 'min' in hints:
                par.min = hints['min']
            if 'max' in hints:
                par.max = hints['max']
            if 'value' in hints:
                par.value = hints['value']
            if 'expr' in hints:
                par.expr = hints['expr']

            pwids = ParameterWidgets(panel, par, name_size=100, expr_size=125,
                                     float_size=80, prefix=prefix,
                                     widgets=('name', 'value',  'minval',
                                              'maxval', 'vary', 'expr'))
            parwids[par.name] = pwids
            panel.Add(pwids.name, newrow=True)

            panel.AddMany((pwids.value, pwids.vary, pwids.bounds,
                           pwids.minval, pwids.maxval, pwids.expr))

        for sname, hint in minst.param_hints.items():
            pname = "%s%s" % (prefix, sname)
            if 'expr' in hint and pname not in parnames:
                par = Parameter(name=pname, value=0, expr=hint['expr'])
                pwids = ParameterWidgets(panel, par, name_size=100, expr_size=225,
                                         float_size=80, prefix=prefix,
                                         widgets=('name', 'value', 'expr'))
                parwids[par.name] = pwids
                panel.Add(pwids.name, newrow=True)
                panel.Add(pwids.value)
                panel.Add(pwids.expr, dcol=4, style=wx.ALIGN_RIGHT)
                pwids.value.Disable()

        fgroup = Group(prefix=prefix, title=title, mclass=mclass,
                       mclass_kws=mclass_kws, usebox=usebox, panel=panel,
                       parwids=parwids, float_size=65, expr_size=150,
                       pick2_msg=pick2msg, bkgbox=bkgbox)


        self.fit_components[prefix] = fgroup
        panel.pack()

        self.mod_nb.AddPage(panel, title, True)
        sx,sy = self.GetSize()
        self.SetSize((sx, sy+1))
        self.SetSize((sx, sy))

    def onDeleteComponent(self, evt=None, prefix=None):
        fgroup = self.fit_components.get(prefix, None)
        if fgroup is None:
            return

        for i in range(self.mod_nb.GetPageCount()):
            if fgroup.title == self.mod_nb.GetPageText(i):
                self.mod_nb.DeletePage(i)

        for attr in dir(fgroup):
            setattr(fgroup, attr, None)

        self.fit_components.pop(prefix)

        # sx,sy = self.GetSize()
        # self.SetSize((sx, sy+1))
        # self.SetSize((sx, sy))

    def onPick2EraseTimer(self, evt=None):
        """erases line trace showing automated 'Pick 2' guess """
        self.pick2erase_timer.Stop()
        panel = self.pick2erase_panel
        ntrace = panel.conf.ntrace - 1
        trace = panel.conf.get_mpl_line(ntrace)
        panel.conf.get_mpl_line(ntrace).set_data(np.array([]), np.array([]))
        panel.conf.ntrace = ntrace
        panel.draw()

    def onPick2Timer(self, evt=None):
        """checks for 'Pick 2' events, and initiates 'Pick 2' guess
        for a model from the selected data range
        """
        try:
            plotframe = self.controller.get_display(stacked=False)
            curhist = plotframe.cursor_hist[:]
            plotframe.Raise()
        except:
            return

        if (time.time() - self.pick2_t0) > self.pick2_timeout:
            msg = self.pick2_group.pick2_msg.SetLabel(" ")
            plotframe.cursor_hist = []
            self.pick2_timer.Stop()
            return

        if len(curhist) < 2:
            self.pick2_group.pick2_msg.SetLabel("%i/2" % (len(curhist)))
            return

        self.pick2_group.pick2_msg.SetLabel("done.")
        self.pick2_timer.Stop()

        # guess param values
        xcur = (curhist[0][0], curhist[1][0])
        xmin, xmax = min(xcur), max(xcur)

        dgroup = getattr(self.larch.symtable, self.controller.groupname)
        x, y = dgroup.xdat, dgroup.ydat
        i0 = index_of(dgroup.xdat, xmin)
        i1 = index_of(dgroup.xdat, xmax)
        x, y = dgroup.xdat[i0:i1+1], dgroup.ydat[i0:i1+1]

        mod = self.pick2_group.mclass(prefix=self.pick2_group.prefix)
        parwids = self.pick2_group.parwids
        try:
            guesses = mod.guess(y, x=x)
        except:
            return

        for name, param in guesses.items():
            if name in parwids:
                parwids[name].value.SetValue(param.value)

        dgroup._tmp = mod.eval(guesses, x=dgroup.xdat)
        plotframe = self.controller.get_display(stacked=False)
        plotframe.cursor_hist = []
        plotframe.oplot(dgroup.xdat, dgroup._tmp)
        self.pick2erase_panel = plotframe.panel

        self.pick2erase_timer.Start(5000)


    def onPick2Points(self, evt=None, prefix=None):
        fgroup = self.fit_components.get(prefix, None)
        if fgroup is None:
            return

        plotframe = self.controller.get_display(stacked=False)
        plotframe.Raise()

        plotframe.cursor_hist = []
        fgroup.npts = 0
        self.pick2_group = fgroup

        if fgroup.pick2_msg is not None:
            fgroup.pick2_msg.SetLabel("0/2")

        self.pick2_t0 = time.time()
        self.pick2_timer.Start(250)


    def onSaveFitResult(self, event=None):
        dgroup = self.controller.get_group()
        deffile = dgroup.filename.replace('.', '_') + '.modl'

        outfile = FileSave(self, 'Save Fit Result',
                           default_file=deffile,
                           wildcard=ModelWcards)

        if outfile is not None:
            try:
                self.save_fit_result(dgroup.fit_history[-1], outfile)
            except IOError:
                print('could not write %s' % outfile)

    def onLoadFitResult(self, event=None):
        mfile = FileOpen(self, 'Load Fit Result',
                         default_file='', wildcard=ModelWcards)
        if mfile is not None:
            self.load_modelresult(mfile)

    def save_fit_result(self, fitresult, outfile):
        """saves a customized ModelResult"""
        save_modelresult(fitresult, outfile)

    def load_modelresult(self, inpfile):
        """read a customized ModelResult"""
        result = load_modelresult(inpfile)

        for prefix in list(self.fit_components.keys()):
            self.onDeleteComponent(self, prefix=prefix)

        for comp in result.model.components:
            isbkg = comp.prefix in result.user_options['bkg_components']
            self.addModel(model=comp.func.__name__,
                          prefix=comp.prefix, isbkg=isbkg)

        for comp in result.model.components:
            parwids = self.fit_components[comp.prefix].parwids
            for pname, par in result.params.items():
                if pname in parwids:
                    wids = parwids[pname]
                    if wids.minval is not None:
                        wids.minval.SetValue(par.min)
                    if wids.maxval is not None:
                        wids.maxval.SetValue(par.max)
                    val = result.init_values.get(pname, par.value)
                    wids.value.SetValue(val)

        self.fill_form_from_dict(result.user_options)
        return result

    def onExportFitResult(self, event=None):
        dgroup = self.controller.get_group()
        deffile = dgroup.filename.replace('.', '_') + '_result.xdi'
        wcards = 'All files (*.*)|*.*'

        outfile = FileSave(self, 'Export Fit Result',
                           default_file=deffile, wildcard=wcards)

        if outfile is not None:
            i1, i2 = self.get_xranges(dgroup.xdat)
            x = dgroup.xdat[i1:i2]
            y = dgroup.ydat[i1:i2]
            yerr = None
            if hasattr(dgroup, 'yerr'):
                yerr = 1.0*dgroup.yerr
                if not isinstance(yerr, np.ndarray):
                    yerr = yerr * np.ones(len(y))
                else:
                    yerr = yerr[i1:i2]

            export_modelresult(dgroup.fit_history[-1],
                               filename=outfile,
                               datafile=dgroup.filename,
                               ydata=y, yerr=yerr, x=x)


    def on_selpoint(self, evt=None, opt='xmin'):
        xval = None
        try:
            xval = self.larch.symtable._plotter.plot1_x
        except:
            return
        wid = getattr(self, opt, None)
        if wid is not None:
            wid.SetValue(xval)

    def get_xranges(self, x):
        opts = self.read_form()
        dgroup = self.controller.get_group()
        en_eps = min(np.diff(dgroup.energy)) / 5.

        i1 = index_of(x, opts['emin'] + en_eps)
        i2 = index_of(x, opts['emax'] + en_eps) + 1
        return i1, i2

    def build_fitmodel(self):
        """ use fit components to build model"""
        dgroup = self.controller.get_group()
        fullmodel = None
        params = Parameters()
        self.summary = {'components': [], 'options': {}}
        peaks = []
        for comp in self.fit_components.values():
            _cen, _amp = None, None
            if comp.usebox is not None and comp.usebox.IsChecked():
                for parwids in comp.parwids.values():
                    params.add(parwids.param)
                    #print(" add param ", parwids.param)
                    if parwids.param.name.endswith('_center'):
                        _cen = parwids.param.name
                    elif parwids.param.name.endswith('_amplitude'):
                        _amp = parwids.param.name

                self.summary['components'].append((comp.mclass.__name__, comp.mclass_kws))
                thismodel = comp.mclass(**comp.mclass_kws)
                if fullmodel is None:
                   fullmodel = thismodel
                else:
                    fullmodel += thismodel
                if not comp.bkgbox.IsChecked() and _cen is not None and _amp is not None:
                    peaks.append((_amp, _cen))

        if len(peaks) > 0:
            denom = '+'.join([p[0] for p in peaks])
            numer = '+'.join(["%s*%s "% p for p in peaks])
            params.add('fit_centroid', expr="(%s)/(%s)" %(numer, denom))

        self.fit_model = fullmodel
        self.fit_params = params

        if dgroup is not None:
            i1, i2 = self.get_xranges(dgroup.xdat)
            xsel = dgroup.xdat[i1:i2]
            dgroup.xfit = xsel
            dgroup.yfit = self.fit_model.eval(self.fit_params, x=xsel)
            dgroup.ycomps = self.fit_model.eval_components(params=self.fit_params,
                                                           x=xsel)
        return dgroup

    def onFitSelected(self, event=None):
        dgroup = self.build_fitmodel()
        opts = self.read_form()
        print("fitting selected groups in progress")

    def onFitModel(self, event=None):
        dgroup = self.build_fitmodel()
        opts = self.read_form()

        i1, i2 = self.get_xranges(dgroup.xdat)
        dgroup.xfit = dgroup.xdat[i1:i2]
        ysel = dgroup.ydat[i1:i2]
        # print('onFit Model : xrange ', i1, i2, len(dgroup.xfit), len(dgroup.yfit))
        weights = np.ones(len(ysel))

        if hasattr(dgroup, 'yerr'):
            yerr = 1.0*dgroup.yerr
            if not isinstance(yerr, np.ndarray):
                yerr = yerr * np.ones(len(ysel))
            else:
                yerr = yerr[i1:i2]
            yerr_min = 1.e-9*ysel.mean()
            yerr[np.where(yerr < yerr_min)] = yerr_min
            weights = 1.0/yerr

        result = self.fit_model.fit(ysel, params=self.fit_params,
                                    x=dgroup.xfit, weights=weights,
                                    method='leastsq')
        self.summary['xmin'] = dgroup.xdat[i1]
        self.summary['xmax'] = dgroup.xdat[i2]
        for attr in ('aic', 'bic', 'chisqr', 'redchi', 'ci_out', 'covar',
                     'flatchain', 'success', 'nan_policy', 'nfev', 'ndata',
                     'nfree', 'nvarys', 'init_values'):
            self.summary[attr] = getattr(result, attr)
        self.summary['params'] = result.params

        dgroup.yfit = result.best_fit
        dgroup.ycomps = self.fit_model.eval_components(params=result.params,
                                                       x=dgroup.xfit)

        result.model_repr = self.fit_model._reprstring(long=True)

        ## hacks to save user options
        result.user_options = opts
        bkg_comps = []
        for label, comp in self.fit_components.items():
            if comp.bkgbox.IsChecked():
                bkg_comps.append(label)
        result.user_options['bkg_components'] = bkg_comps

        self.autosave_modelresult(result)
        if not hasattr(dgroup, 'fit_history'):
            dgroup.fit_history = []

        dgroup.fit_history.append(result)
        self.plot_choice.SetStringSelection(PLOT_FIT)
        self.onPlot()

        self.parent.show_subframe('result_frame', FitResultFrame,
                                  datagroup=dgroup, peakframe=self)

        self.parent.subframes['result_frame'].show_fitresult()
        [m.Enable(True) for m in self.parent.afterfit_menus]

    def update_start_values(self, params):
        """fill parameters with best fit values"""
        allparwids = {}
        for comp in self.fit_components.values():
            if comp.usebox is not None and comp.usebox.IsChecked():
                for name, parwids in comp.parwids.items():
                    allparwids[name] = parwids

        for pname, par in params.items():
            if pname in allparwids:
                allparwids[pname].value.SetValue(par.value)

    def autosave_modelresult(self, result, fname=None):
        """autosave model result to user larch folder"""
        xasguidir = os.path.join(site_config.usr_larchdir, 'xasgui')
        if not os.path.exists(xasguidir):
            try:
                os.makedirs(xasguidir)
            except OSError:
                print("Warning: cannot create XAS GUI user folder")
                return
        if not HAS_MODELSAVE:
            print("Warning: cannot save model results: upgrade lmfit")
            return
        if fname is None:
            fname = 'autosave.fitresult'
        fname = os.path.join(xasguidir, fname)

        self.save_fit_result(result, fname)
Esempio n. 8
0
class ScanViewerFrame(wx.Frame):
    _about = """Scan 2D Plotter
  Matt Newville <newville @ cars.uchicago.edu>
  """

    def __init__(self, parent=None, size=(850, 650), _larch=None, **kws):
        wx.Frame.__init__(self, parent, -1, size=size, style=FRAMESTYLE)
        self.file_groups = {}
        self.last_array_sel = {}
        title = "Column Data File Viewer"
        self.larch = _larch
        self.larch_buffer = None
        self.subframes = {}
        self.plotframe = None
        self.groupname = None
        self.SetTitle(title)
        self.SetSize(size)
        self.SetFont(Font(10))

        self.config = {'chdir_on_fileopen': True}

        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)
        read_workdir('scanviewer.dat')
        self.need_xas_update = False
        self.xas_timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.onXASProcessTimer, self.xas_timer)
        self.xas_timer.Start(500)

    def createMainPanel(self):
        splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
        splitter.SetMinimumPaneSize(225)

        self.filelist = EditableCheckListBox(splitter)
        self.filelist.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.filelist.Bind(wx.EVT_LISTBOX, self.ShowFile)

        self.detailspanel = self.createDetailsPanel(splitter)

        splitter.SplitVertically(self.filelist, self.detailspanel, 1)
        wx.CallAfter(self.init_larch)

    def createDetailsPanel(self, parent):
        mainpanel = wx.Panel(parent)
        mainsizer = wx.BoxSizer(wx.VERTICAL)
        panel = wx.Panel(mainpanel)
        sizer = wx.GridBagSizer(8, 7)

        self.title = SimpleText(panel, 'initializing...')
        ir = 0
        sizer.Add(self.title, (ir, 0), (1, 6), LCEN, 2)
        # x-axis

        self.plot_one = Button(panel,
                               'Plot',
                               size=(200, 30),
                               action=self.onPlotOne)
        self.plot_sel = Button(panel,
                               'Plot Selected',
                               size=(200, 30),
                               action=self.onPlotSel)

        ir += 1
        sizer.Add(self.plot_one, (ir, 0), (1, 2), LCEN, 0)
        sizer.Add(self.plot_sel, (ir, 2), (1, 2), LCEN, 0)
        pack(panel, sizer)

        mainsizer.Add(panel, 0, LCEN | wx.EXPAND, 2)

        o = """
        btnbox   = wx.Panel(mainpanel)
        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
        for ttl, opt in (('New Plot',   'new'),
                         ('Over Plot (left)',  'left'),
                         ('Over Plot (right)', 'right')):

            btnsizer.Add(Button(btnbox, ttl, size=(135, -1),
                                action=partial(self.onPlot, opt=opt)),
                         LCEN, 1)

        pack(btnbox, btnsizer)
        mainsizer.Add(btnbox, 0, LCEN, 2)
        """

        self.nb = flat_nb.FlatNotebook(mainpanel, -1, agwStyle=FNB_STYLE)

        self.nb.SetTabAreaColour(wx.Colour(248, 248, 240))
        self.nb.SetActiveTabColour(wx.Colour(254, 254, 195))

        self.nb.SetNonActiveTabTextColour(wx.Colour(40, 40, 180))
        self.nb.SetActiveTabTextColour(wx.Colour(80, 0, 0))

        self.xas_panel = self.CreateXASPanel(self.nb)  # mainpanel)
        self.fit_panel = self.CreateFitPanel(self.nb)  # mainpanel)

        self.nb.AddPage(self.fit_panel, ' General Curve Fitting ', True)
        self.nb.AddPage(self.xas_panel, ' XAS Processing ', True)

        mainsizer.Add(self.nb, 1, LCEN | wx.EXPAND, 2)

        pack(mainpanel, mainsizer)

        return mainpanel

    def CreateFitPanel(self, parent):
        panel = wx.Panel(parent)
        tpan = wx.Panel(panel)
        self.fit_model = Choice(tpan,
                                size=(100, -1),
                                choices=('Gaussian', 'Lorentzian', 'Voigt',
                                         'Linear', 'Quadratic', 'Step',
                                         'Rectangle', 'Exponential'))
        self.fit_bkg = Choice(tpan,
                              size=(100, -1),
                              choices=('None', 'constant', 'linear',
                                       'quadratic'))
        self.fit_step = Choice(tpan,
                               size=(100, -1),
                               choices=('linear', 'error function', 'arctan'))

        tsizer = wx.GridBagSizer(10, 4)
        tsizer.Add(SimpleText(tpan, 'Fit Model: '), (0, 0), (1, 1), LCEN)
        tsizer.Add(self.fit_model, (0, 1), (1, 1), LCEN)

        tsizer.Add(SimpleText(tpan, 'Background: '), (0, 2), (1, 1), LCEN)
        tsizer.Add(self.fit_bkg, (0, 3), (1, 1), LCEN)

        tsizer.Add(
            Button(tpan, 'Show Fit', size=(100, -1), action=self.onFitPeak),
            (1, 1), (1, 1), LCEN)

        tsizer.Add(SimpleText(tpan, 'Step Form: '), (1, 2), (1, 1), LCEN)
        tsizer.Add(self.fit_step, (1, 3), (1, 1), LCEN)

        pack(tpan, tsizer)

        self.fit_report = RichTextCtrl(panel,
                                       size=(525, 250),
                                       style=wx.VSCROLL | wx.NO_BORDER)

        self.fit_report.SetEditable(False)
        self.fit_report.SetFont(Font(9))

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(tpan, 0, wx.GROW | wx.ALL, 2)
        sizer.Add(self.fit_report, 1, LCEN | wx.GROW, 2)
        pack(panel, sizer)
        return panel

    def InitializeXASPanel(self, dgroup):
        predefs = dict(e0=0,
                       pre1=-200,
                       pre2=-30,
                       norm1=50,
                       edge_step=0,
                       norm2=-10,
                       nnorm=3,
                       nvict=2,
                       auto_step=True,
                       auto_e0=True,
                       show_e0=True)

        if hasattr(dgroup, 'pre_edge_details'):
            predefs.update(group2dict(dgroup.pre_edge_details))

        self.xas_e0.SetValue(predefs['e0'])
        self.xas_step.SetValue(predefs['edge_step'])
        self.xas_pre1.SetValue(predefs['pre1'])
        self.xas_pre2.SetValue(predefs['pre2'])
        self.xas_nor1.SetValue(predefs['norm1'])
        self.xas_nor2.SetValue(predefs['norm2'])
        self.xas_vict.SetSelection(predefs['nvict'])
        self.xas_nnor.SetSelection(predefs['nnorm'])

        self.xas_showe0.SetValue(predefs['show_e0'])
        self.xas_autoe0.SetValue(predefs['auto_e0'])
        self.xas_autostep.SetValue(predefs['auto_step'])

    def CreateXASPanel(self, parent):
        opchoices = ('Raw Data', 'Normalized', 'Derivative',
                     'Normalized + Derivative', 'Pre-edge subtracted',
                     'Raw Data + Pre-edge/Post-edge')
        p = panel = wx.Panel(parent)
        opts = {'action': self.UpdateXASPlot}
        self.xas_autoe0 = Check(panel, default=True, label='auto?', **opts)
        self.xas_showe0 = Check(panel, default=True, label='show?', **opts)
        self.xas_autostep = Check(panel, default=True, label='auto?', **opts)
        self.xas_op = Choice(panel, size=(300, -1), choices=opchoices, **opts)

        self.btns = {}
        for name in ('e0', 'pre1', 'pre2', 'nor1', 'nor2'):
            bb = BitmapButton(panel,
                              get_icon('plus'),
                              action=partial(self.onXAS_selpoint, opt=name),
                              tooltip='use last point selected from plot')
            self.btns[name] = bb

        opts = {'size': (85, -1), 'precision': 3, 'action': self.UpdateXASPlot}
        self.xwids = {}
        self.xas_e0 = FloatCtrl(panel, value=0, **opts)
        self.xas_step = FloatCtrl(panel, value=0, **opts)
        opts['precision'] = 1
        self.xas_pre1 = FloatCtrl(panel, value=-200, **opts)
        self.xas_pre2 = FloatCtrl(panel, value=-30, **opts)
        self.xas_nor1 = FloatCtrl(panel, value=50, **opts)
        self.xas_nor2 = FloatCtrl(panel, value=-50, **opts)

        opts = {
            'size': (50, -1),
            'choices': ('0', '1', '2', '3'),
            'action': self.UpdateXASPlot
        }
        self.xas_vict = Choice(panel, **opts)
        self.xas_nnor = Choice(panel, **opts)
        self.xas_vict.SetSelection(1)
        self.xas_nnor.SetSelection(2)
        sizer = wx.GridBagSizer(10, 7)

        sizer.Add(SimpleText(p, 'Plot XAS as: '), (0, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'E0 : '), (1, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Edge Step: '), (2, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Pre-edge range: '), (3, 0), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'Normalization range: '), (4, 0), (1, 1), LCEN)

        sizer.Add(self.xas_op, (0, 1), (1, 6), LCEN)
        sizer.Add(self.btns['e0'], (1, 1), (1, 1), LCEN)
        sizer.Add(self.xas_e0, (1, 2), (1, 1), LCEN)
        sizer.Add(self.xas_autoe0, (1, 3), (1, 3), LCEN)
        sizer.Add(self.xas_showe0, (1, 6), (1, 2), LCEN)

        sizer.Add(self.xas_step, (2, 2), (1, 1), LCEN)
        sizer.Add(self.xas_autostep, (2, 3), (1, 3), LCEN)

        sizer.Add(self.btns['pre1'], (3, 1), (1, 1), LCEN)
        sizer.Add(self.xas_pre1, (3, 2), (1, 1), LCEN)
        sizer.Add(SimpleText(p, ':'), (3, 3), (1, 1), LCEN)
        sizer.Add(self.btns['pre2'], (3, 4), (1, 1), LCEN)
        sizer.Add(self.xas_pre2, (3, 5), (1, 1), LCEN)
        sizer.Add(self.btns['nor1'], (4, 1), (1, 1), LCEN)
        sizer.Add(self.xas_nor1, (4, 2), (1, 1), LCEN)
        sizer.Add(SimpleText(p, ':'), (4, 3), (1, 1), LCEN)
        sizer.Add(self.btns['nor2'], (4, 4), (1, 1), LCEN)
        sizer.Add(self.xas_nor2, (4, 5), (1, 1), LCEN)

        sizer.Add(SimpleText(p, 'Victoreen:'), (3, 6), (1, 1), LCEN)
        sizer.Add(self.xas_vict, (3, 7), (1, 1), LCEN)
        sizer.Add(SimpleText(p, 'PolyOrder:'), (4, 6), (1, 1), LCEN)
        sizer.Add(self.xas_nnor, (4, 7), (1, 1), LCEN)

        pack(panel, sizer)
        return panel

    def onXAS_selpoint(self, evt=None, opt='e0'):
        xval = None
        try:
            xval = self.larch.symtable._plotter.plot1_x
        except:
            pass
        if xval is None:
            return

        e0 = self.xas_e0.GetValue()
        if opt == 'e0':
            self.xas_e0.SetValue(xval)
            self.xas_autoe0.SetValue(0)
        elif opt == 'pre1':
            self.xas_pre1.SetValue(xval - e0)
        elif opt == 'pre2':
            self.xas_pre2.SetValue(xval - e0)
        elif opt == 'nor1':
            self.xas_nor1.SetValue(xval - e0)
        elif opt == 'nor2':
            self.xas_nor2.SetValue(xval - e0)

    def onCustomColumns(self, evt=None):
        pass

    def onFitPeak(self, evt=None):
        gname = self.groupname

        dtext = []
        model = self.fit_model.GetStringSelection().lower()
        dtext.append('Fit Model: %s' % model)
        bkg = self.fit_bkg.GetStringSelection()
        if bkg == 'None':
            bkg = None
        if bkg is None:
            dtext.append('No Background')
        else:
            dtext.append('Background: %s' % bkg)

        step = self.fit_step.GetStringSelection().lower()
        if model in ('step', 'rectangle'):
            dtext.append('Step form: %s' % step)

        try:
            lgroup = getattr(self.larch.symtable, gname)
            x = lgroup._xdat
            y = lgroup._ydat
        except AttributeError:
            self.write_message('need data to fit!')
            return
        if step.startswith('error'):
            step = 'erf'
        elif step.startswith('arctan'):
            step = 'atan'

        pgroup = fit_peak(x,
                          y,
                          model,
                          background=bkg,
                          step=step,
                          _larch=self.larch)

        dtext = '\n'.join(dtext)
        dtext = '%s\n%s\n' % (
            dtext, fit_report(
                pgroup.params, min_correl=0.25, _larch=self.larch))

        self.fit_report.SetEditable(True)
        self.fit_report.SetValue(dtext)
        self.fit_report.SetEditable(False)

        lgroup.plot_yarrays = [(lgroup._ydat, PLOTOPTS_1, lgroup.plot_ylabel)]
        if bkg is None:
            lgroup._fit = pgroup.fit[:]
            lgroup.plot_yarrays.append((lgroup._fit, PLOTOPTS_2, 'fit'))
        else:
            lgroup._fit = pgroup.fit[:]
            lgroup._fit_bgr = pgroup.bkg[:]
            lgroup.plot_yarrays.append((lgroup._fit, PLOTOPTS_2, 'fit'))
            lgroup.plot_yarrays.append(
                (lgroup._fit_bgr, PLOTOPTS_2, 'background'))
        self.plot_group(gname, new=True)

    def xas_process(self, gname, new_mu=False, **kws):
        """ process (pre-edge/normalize) XAS data from XAS form, overwriting
        larch group '_y1_' attribute to be plotted
        """
        dgroup = getattr(self.larch.symtable, gname)

        if not hasattr(dgroup, 'energy'):
            dgroup.energy = dgroup._xdat
        if not hasattr(dgroup, 'mu'):
            dgroup.mu = dgroup._ydat

        e0 = None
        if not self.xas_autoe0.IsChecked():
            _e0 = self.xas_e0.GetValue()
            if _e0 < max(dgroup.energy) and _e0 > min(dgroup.energy):
                e0 = float(_e0)

        preopts = {'e0': e0}
        if not self.xas_autostep.IsChecked():
            preopts['step'] = self.xas_step.GetValue()
        preopts['pre1'] = self.xas_pre1.GetValue()
        preopts['pre2'] = self.xas_pre2.GetValue()
        preopts['norm1'] = self.xas_nor1.GetValue()
        preopts['norm2'] = self.xas_nor2.GetValue()
        preopts['nvict'] = self.xas_vict.GetSelection()
        preopts['nnorm'] = self.xas_nnor.GetSelection()
        preopts['make_flat'] = False
        preopts['_larch'] = self.larch

        pre_edge(dgroup, **preopts)
        dgroup.pre_edge_details.e0 = dgroup.e0
        dgroup.pre_edge_details.edge_step = dgroup.edge_step
        dgroup.pre_edge_details.auto_e0 = self.xas_autoe0.IsChecked()
        dgroup.pre_edge_details.show_e0 = self.xas_showe0.IsChecked()
        dgroup.pre_edge_details.auto_step = self.xas_autostep.IsChecked()

        if self.xas_autoe0.IsChecked():
            self.xas_e0.SetValue(dgroup.e0)
        if self.xas_autostep.IsChecked():
            self.xas_step.SetValue(dgroup.edge_step)

        details_group = dgroup.pre_edge_details
        self.xas_pre1.SetValue(details_group.pre1)
        self.xas_pre2.SetValue(details_group.pre2)
        self.xas_nor1.SetValue(details_group.norm1)
        self.xas_nor2.SetValue(details_group.norm2)

        dgroup.orig_ylabel = dgroup.plot_ylabel
        dgroup.plot_ylabel = '$\mu$'
        dgroup.plot_y2label = None
        dgroup.plot_xlabel = '$E \,\mathrm{(eV)}$'
        dgroup.plot_yarrays = [(dgroup.mu, PLOTOPTS_1, dgroup.plot_ylabel)]
        y4e0 = dgroup.mu

        out = self.xas_op.GetStringSelection().lower()  # raw, pre, norm, flat
        if out.startswith('raw data + pre'):
            dgroup.plot_yarrays = [(dgroup.mu, PLOTOPTS_1, '$\mu$'),
                                   (dgroup.pre_edge, PLOTOPTS_2, 'pre edge'),
                                   (dgroup.post_edge, PLOTOPTS_2, 'post edge')]
        elif out.startswith('pre'):
            dgroup.pre_edge_sub = dgroup.norm * dgroup.edge_step
            dgroup.plot_yarrays = [(dgroup.pre_edge_sub, PLOTOPTS_1,
                                    'pre-edge subtracted $\mu$')]
            y4e0 = dgroup.pre_edge_sub
            dgroup.plot_ylabel = 'pre-edge subtracted $\mu$'
        elif 'norm' in out and 'deriv' in out:
            dgroup.plot_yarrays = [(dgroup.norm, PLOTOPTS_1,
                                    'normalized $\mu$'),
                                   (dgroup.dmude, PLOTOPTS_D, '$d\mu/dE$')]
            y4e0 = dgroup.norm
            dgroup.plot_ylabel = 'normalized $\mu$'
            dgroup.plot_y2label = '$d\mu/dE$'
        elif out.startswith('norm'):
            dgroup.plot_yarrays = [(dgroup.norm, PLOTOPTS_1,
                                    'normalized $\mu$')]
            y4e0 = dgroup.norm
            dgroup.plot_ylabel = 'normalized $\mu$'
        elif out.startswith('deriv'):
            dgroup.plot_yarrays = [(dgroup.dmude, PLOTOPTS_1, '$d\mu/dE$')]
            y4e0 = dgroup.dmude
            dgroup.plot_ylabel = '$d\mu/dE$'

        dgroup.plot_ymarkers = []
        if self.xas_showe0.IsChecked():
            ie0 = index_of(dgroup._xdat, dgroup.e0)
            dgroup.plot_ymarkers = [(dgroup.e0, y4e0[ie0], {'label': 'e0'})]
        return

    def init_larch(self):
        t0 = time.time()
        if self.larch is None:
            self.larch = Interpreter()
        self.larch.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp())
        self.larch.symtable.set_symbol('_sys.wx.parent', self)

        self.SetStatusText('ready')
        self.title.SetLabel('')

    def write_message(self, s, panel=0):
        """write a message to the Status Bar"""
        self.SetStatusText(s, panel)

    def onXASProcessTimer(self, evt=None):
        if self.groupname is None:
            return
        if self.need_xas_update:
            self.xas_process(self.groupname)
            self.plot_group(self.groupname, new=True)
            self.need_xas_update = False

    def UpdateXASPlot(self, evt=None, **kws):
        self.need_xas_update = True

    def onPlotOne(self, evt=None, groupname=None):
        if groupname is None:
            groupname = self.groupname

        dgroup = getattr(self.larch.symtable, groupname, None)
        if dgroup is None:
            return
        self.groupname = groupname
        if (dgroup.is_xas and (getattr(dgroup, 'plot_yarrays', None) is None
                               or getattr(dgroup, 'energy', None) is None
                               or getattr(dgroup, 'mu', None) is None)):
            self.xas_process(groupname)
        self.plot_group(groupname, new=True)

    def onPlotSel(self, evt=None):
        newplot = True
        group_ids = self.filelist.GetCheckedStrings()
        for checked in group_ids:
            groupname = self.file_groups[str(checked)]
            dgroup = getattr(self.larch.symtable, groupname, None)
            if dgroup is None:
                continue
            if dgroup.is_xas:
                if ((getattr(dgroup, 'plot_yarrays', None) is None
                     or getattr(dgroup, 'energy', None) is None
                     or getattr(dgroup, 'mu', None) is None)):
                    self.xas_process(groupname)

                dgroup.plot_yarrays = [(dgroup.norm, PLOTOPTS_1,
                                        '%s norm' % dgroup._filename)]
                dgroup.plot_ylabel = 'normalized $\mu$'
                dgroup.plot_xlabel = '$E\,\mathrm{(eV)}$'
                dgroup.plot_ymarkers = []

            else:
                dgroup.plot_yarrays = [(dgroup._ydat, PLOTOPTS_1,
                                        dgroup._filename)]

            self.plot_group(groupname, title='', new=newplot)
            newplot = False

    def plot_group(self, groupname, title=None, new=True):

        oplot = self.larch.symtable._plotter.plot
        newplot = self.larch.symtable._plotter.newplot
        getdisplay = self.larch.symtable._plotter.get_display

        plotcmd = oplot
        if new:
            plotcmd = newplot

        dgroup = getattr(self.larch.symtable, groupname, None)
        if not hasattr(dgroup, '_xdat'):
            print("Cannot plot group ", groupname)

        if hasattr(dgroup, 'plot_yarrays'):
            plot_yarrays = dgroup.plot_yarrays
        else:
            plot_yarrays = [(dgroup._ydat, {}, None)]
        # print("Plt Group ", groupname, hasattr(dgroup,'plot_yarrays'))

        popts = {}
        path, fname = os.path.split(dgroup.filename)
        popts['label'] = "%s: %s" % (fname, dgroup.plot_ylabel)
        popts['xlabel'] = dgroup.plot_xlabel
        popts['ylabel'] = dgroup.plot_ylabel
        if getattr(dgroup, 'plot_y2label', None) is not None:
            popts['y2label'] = dgroup.plot_y2label

        if plotcmd == newplot and title is None:
            title = fname

        popts['title'] = title

        for yarr in plot_yarrays:
            popts.update(yarr[1])
            if yarr[2] is not None:
                popts['label'] = yarr[2]
            plotcmd(dgroup._xdat, yarr[0], **popts)
            plotcmd = oplot  # self.plotpanel.oplot

        ppanel = getdisplay(_larch=self.larch).panel
        if hasattr(dgroup, 'plot_ymarkers'):
            axes = ppanel.axes
            for x, y, opts in dgroup.plot_ymarkers:
                popts = {
                    'marker': 'o',
                    'markersize': 4,
                    'markerfacecolor': 'red',
                    'markeredgecolor': 'black'
                }
                popts.update(opts)
                axes.plot([x], [y], **popts)
        ppanel.canvas.draw()

    def onShowLarchBuffer(self, evt=None):
        if self.larch_buffer is None:
            self.larch_buffer = larchframe.LarchFrame(_larch=self.larch)

        self.larch_buffer.Show()
        self.larch_buffer.Raise()

    def ShowFile(self, evt=None, groupname=None, **kws):
        if groupname is None and evt is not None:
            groupname = self.file_groups[str(evt.GetString())]

        if not hasattr(self.larch.symtable, groupname):
            print('Error reading file ', groupname)
            return

        self.groupname = groupname
        self.dgroup = getattr(self.larch.symtable, groupname, None)

        if self.dgroup.is_xas:
            self.nb.SetSelection(1)
        else:
            self.nb.SetSelection(0)

    def createMenus(self):
        # ppnl = self.plotpanel
        self.menubar = wx.MenuBar()
        #
        fmenu = wx.Menu()
        MenuItem(self, fmenu, "&Open Data File\tCtrl+O", "Read Scan File",
                 self.onReadScan)

        MenuItem(self, fmenu, "Show Larch Buffer\tCtrl+L",
                 "Show Larch Programming Buffer", self.onShowLarchBuffer)

        fmenu.AppendSeparator()

        MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose)

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

        omenu = wx.Menu()
        MenuItem(self, omenu, "Edit Column Labels\tCtrl+E",
                 "Edit Column Labels", self.onEditColumnLabels)

        self.menubar.Append(omenu, "Options")

        self.SetMenuBar(self.menubar)
        self.Bind(wx.EVT_CLOSE, self.onClose)

    def onAbout(self, evt):
        dlg = wx.MessageDialog(self, self._about, "About ScanViewer",
                               wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onClose(self, evt):
        save_workdir('scanviewer.dat')

        for nam in dir(self.larch.symtable._plotter):
            obj = getattr(self.larch.symtable._plotter, nam)
            try:
                obj.Destroy()
            except:
                pass

        for nam in dir(self.larch.symtable._sys.wx):
            obj = getattr(self.larch.symtable._sys.wx, nam)
            del obj

        if self.larch_buffer is not None:
            try:
                self.larch_buffer.onClose()
            except:
                pass
        for w in self.GetChildren():
            w.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 onEditColumnLabels(self, evt=None):
        self.show_subframe('coledit',
                           EditColumnFrame,
                           group=self.dgroup,
                           last_array_sel=self.last_array_sel,
                           read_ok_cb=self.onReadScan_Success)

    def onReadScan(self, evt=None):
        dlg = wx.FileDialog(self,
                            message="Load Column Data File",
                            defaultDir=os.getcwd(),
                            wildcard=FILE_WILDCARDS,
                            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            path = path.replace('\\', '/')
            if path in self.file_groups:
                if wx.ID_YES != popup(self, "Re-read file '%s'?" % path,
                                      'Re-read file?'):
                    return

            filedir, filename = os.path.split(path)
            pref = fix_varname((filename + '____')[:7]).replace('.', '_')
            count, maxcount = 1, 9999
            groupname = "%s%3.3i" % (pref, count)
            while hasattr(self.larch.symtable, groupname) and count < maxcount:
                count += 1
                groupname = '%s%3.3i' % (pref, count)

            if self.config['chdir_on_fileopen']:
                os.chdir(filedir)

            fh = open(path, 'r')
            line1 = fh.readline().lower()
            fh.close()
            reader = read_ascii
            if 'epics stepscan file' in line1:
                reader = read_gsexdi
            elif 'epics scan' in line1:
                reader = gsescan_group
            elif 'xdi' in line1:
                reader = read_xdi

            dgroup = reader(str(path), _larch=self.larch)
            if reader == gsescan_group:
                assign_gsescan_groups(dgroup)
            dgroup._path = path
            dgroup._filename = filename
            dgroup._groupname = groupname

            self.show_subframe('coledit',
                               EditColumnFrame,
                               group=dgroup,
                               last_array_sel=self.last_array_sel,
                               read_ok_cb=self.onReadScan_Success)

        dlg.Destroy()

    def onReadScan_Success(self, datagroup, array_sel):
        """ called when column data has been selected and is ready to be used"""
        self.last_array_sel = array_sel
        filename = datagroup._filename
        groupname = datagroup._groupname
        # print("   storing datagroup ", datagroup, groupname, filename)
        # file /group may already exist in list
        if filename in self.file_groups:
            for i in range(1, 101):
                ftest = "%s (%i)" % (filename, i)
                if ftest not in self.groups:
                    filename = ftest
                    break

        if filename not in self.file_groups:
            self.filelist.Append(filename)
            self.file_groups[filename] = groupname

        setattr(self.larch.symtable, groupname, datagroup)
        if datagroup.is_xas:
            self.nb.SetSelection(1)
            self.InitializeXASPanel(datagroup)
        else:
            self.nb.SetSelection(0)
        self.onPlotOne(groupname=groupname)