Beispiel #1
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()
Beispiel #2
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)
Beispiel #3
0
class DemoFrame(wx.Frame):
    def __init__(self):

        wx.Frame.__init__(self,
                          None,
                          -1,
                          'wxutil demo',
                          style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER
                          | wx.TAB_TRAVERSAL)
        self.SetTitle('wxutil demo')

        self.SetFont(Font(11))

        self.set_menu()
        self.statusbar = self.CreateStatusBar(2, 1)
        self.statusbar.SetStatusWidths([-2, -1])
        statusbar_fields = ['Initializing....', ' ']
        for i in range(len(statusbar_fields)):
            self.statusbar.SetStatusText(statusbar_fields[i], i)

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

        panel = GridPanel(self, nrows=8, ncols=4)

        tctrl_name = TextCtrl(panel,
                              value='',
                              action=self.onName,
                              size=(250, -1))

        lctrl_addr = LabeledTextCtrl(self,
                                     value='<>',
                                     action=self.onAddr,
                                     labeltext=' Address: ',
                                     size=(250, -1))

        lab3 = HyperText(panel,
                         ' FloatCtrl: ',
                         size=(100, -1),
                         action=self.onHyperText)

        val3 = FloatCtrl(panel,
                         '3',
                         action=self.onFloat1,
                         precision=2,
                         minval=0,
                         maxval=1000,
                         size=(250, -1))

        lab4 = HyperText(panel,
                         ' FloatSpin: ',
                         size=(100, -1),
                         action=self.onHyperText)
        val4 = FloatSpin(panel,
                         '12.2',
                         action=self.onFloatSpin,
                         digits=2,
                         increment=0.1,
                         size=(250, -1))

        labx = HyperText(panel,
                         ' NumericCombo: ',
                         size=(100, -1),
                         action=self.onHyperText)

        steps = make_steps(prec=1, tmin=0, tmax=100)
        valx = NumericCombo(panel, steps, precision=1)

        self.choice1 = Choice(panel, size=(200, -1), action=self.onChoice)
        self.choice1.SetChoices(['Apple', 'Banana', 'Cherry'])

        yesno = YesNo(panel)

        check1 = Check(panel, label='enable? ', action=self.onCheck)

        btn1 = Button(panel,
                      label='Start',
                      size=(100, -1),
                      action=self.onStart)

        pinbtn = BitmapButton(panel,
                              get_icon('pin'),
                              size=(50, -1),
                              action=partial(self.onBMButton, opt='pin1'),
                              tooltip='use last point selected from plot')

        togbtn = ToggleButton(panel,
                              'Press Me',
                              action=self.onToggleButton,
                              size=(100, -1),
                              tooltip='do it, do it now, you will like it')

        browse_btn = Button(panel,
                            'Open File',
                            action=self.onFileOpen,
                            size=(150, -1))

        okcancel = OkCancel(panel, onOK=self.onOK, onCancel=self.onCancel)

        ptable_btn = Button(panel,
                            'Show Periodic Table',
                            action=self.onPTable,
                            size=(175, -1))

        edlist_btn = Button(panel,
                            'Show Editable Listbox',
                            action=self.onEdList,
                            size=(175, -1))

        filelist_btn = Button(panel,
                              'Show File CheckList',
                              action=self.onFileList,
                              size=(175, -1))

        panel.AddText(' Name: ', style=LEFT)

        panel.Add(tctrl_name, dcol=2)
        panel.Add(lctrl_addr.label, newrow=True)
        panel.Add(lctrl_addr, dcol=2)

        panel.Add(lab3, newrow=True)
        panel.Add(val3, dcol=3)

        panel.Add(lab4, newrow=True)
        panel.Add(val4, dcol=3)

        panel.Add(labx, newrow=True)
        panel.Add(valx, dcol=3)

        panel.AddText(' Choice : ', newrow=True)
        panel.Add(check1)
        panel.Add(self.choice1)
        panel.AddText(' Yes or No: ', newrow=True)
        panel.Add(yesno)
        panel.Add(HLine(panel, size=(500, -1)), dcol=3, newrow=True)

        panel.Add(btn1, newrow=True)
        panel.Add(pinbtn)
        panel.Add(togbtn)

        panel.Add(browse_btn, newrow=True)
        panel.Add(ptable_btn)
        panel.Add(edlist_btn, newrow=True)
        panel.Add(filelist_btn)

        panel.Add(okcancel, newrow=True)

        panel.pack()

        self.timer = wx.Timer(self)
        self.last_time = time.time()
        self.Bind(wx.EVT_TIMER, self.onTimer, self.timer)

        fsizer = wx.BoxSizer(wx.VERTICAL)
        fsizer.Add(panel, 0, LEFT | wx.EXPAND)
        wx.CallAfter(self.init_timer)

        psize = panel.GetBestSize()
        self.SetSize((psize[0] + 5, psize[1] + 25))

        pack(self, fsizer)
        self.Refresh()

    def init_timer(self):
        self.timer.Start(500)

    def onTimer(self, event):
        idle_time = (time.time() - self.last_time)
        msg = "Time remaining = %.1f sec" % (30 - idle_time)
        self.statusbar.SetStatusText(msg, 1)
        if (time.time() - self.last_time) > 30:
            print("quitting...")
            self.onExit()

    def report(self, reason, value):
        self.statusbar.SetStatusText("%s: %s" % (reason, value), 0)
        self.last_time = time.time()

    def set_menu(self):
        menu = wx.Menu()
        mexit = MenuItem(self, menu, "Q&uit", "Quit Program", self.onExit)

        menubar = wx.MenuBar()
        menubar.Append(menu, "&File")
        self.SetMenuBar(menubar)

    def onOK(self, event=None):
        self.report("on OK: ", '')

    def onCancel(self, event=None):
        self.report("on Cancel: ", '')

    def onName(self, event=None):
        self.report("on Name: ", event)

    def onAddr(self, event=None):
        self.report("on Addr: ", event)

    def onHyperText(self, event=None, label=None):
        self.report("on HyperText ", label)

    def onFloat1(self, value=None):
        self.report("on Float1 ", value)

    def onFloatSpin(self, event=None):
        self.report("on FloatSpin ", event.GetString())

    def onChoice(self, event=None):
        self.report("on Choice ", event.GetString())

    def onCheck(self, event=None):
        self.report("on Check ", event.IsChecked())

        self.choice1.Enable(event.IsChecked())

    def onStart(self, event=None):
        self.report("on Start Button ", '')

    def onBMButton(self, event=None, opt='xxx'):
        self.report("on Bitmap Button ", opt)

    def onToggleButton(self, event=None):
        self.report(" on Toggle Button %s " % event.GetString(),
                    event.IsChecked())

    def onPTable(self, event=None):
        PTableFrame(fontsize=10).Show()

    def onEdList(self, event=None):
        frame = wx.Frame(self)
        edlist = EditableListBox(frame, self.onEdListSelect)
        edlist.Append(" Item 1 ")
        edlist.Append(" Item 2 ")
        edlist.Append(" Next ")

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(edlist, 1, wx.EXPAND | wx.ALL, 5)
        pack(frame, sizer)

        frame.Show()
        frame.Raise()

    def onEdListSelect(self, event=None, **kws):
        self.report(" Editable List selected ", event.GetString())

    def onFileList(self, event=None):
        frame = wx.Frame(self)
        edlist = FileCheckList(frame, select_action=self.onFileListSelect)
        for i in range(8):
            edlist.Append("File.%3.3d" % (i + 1))

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(edlist, 1, wx.EXPAND | wx.ALL, 5)
        pack(frame, sizer)

        frame.Show()
        frame.Raise()

    def onFileListSelect(self, event=None, **kws):
        self.report(" File List selected ", event.GetString())

    def onFileOpen(self, event=None):
        wildcards = "%s|%s" % (PY_FILES, ALL_FILES)

        dlg = wx.FileDialog(self,
                            message='Select File',
                            wildcard=wildcards,
                            style=wx.FD_OPEN)

        if dlg.ShowModal() == wx.ID_OK:
            path = os.path.abspath(dlg.GetPath())
            self.report("file ", path)
        dlg.Destroy()

    def onExit(self, event=None):
        self.Destroy()