Example #1
0
class LarchSession(object):
    def __init__(self):
        self._larch = Interpreter()
        self.input  = InputText(prompt='test>', _larch=self._larch)
        self.symtable = self._larch.symtable
        self.symtable.set_symbol('_plotter.newplot',  nullfunction)
        self.symtable.set_symbol('_plotter.plot',     nullfunction)
        self.symtable.set_symbol('_plotter.oplot',    nullfunction)
        self.symtable.set_symbol('_plotter.imshow',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_text',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_arrow',   nullfunction)
        self.symtable.set_symbol('_plotter.xrfplot',   nullfunction)
        
    def run(self, text):
        self.input.put(text)
        ret = None
        while len(self.input) > 0:
            block, fname, lineno = self.input.get()
            if len(block) <= 0:
                continue
            ret = self._larch.eval(block, fname=fname, lineno=lineno)
            if self._larch.error:
                break
        return ret


    def get_errors(self):
        return self._larch.error

    def get_symbol(self, name):
        return self.symtable.get_symbol(name, create=False)
Example #2
0
class LarchSession(object):
    def __init__(self):
        self._larch = Interpreter()
        self.input  = InputText(prompt='test>', _larch=self._larch)
        self.symtable = self._larch.symtable
        self.symtable.set_symbol('_plotter.newplot',  nullfunction)
        self.symtable.set_symbol('_plotter.plot',     nullfunction)
        self.symtable.set_symbol('_plotter.oplot',    nullfunction)
        self.symtable.set_symbol('_plotter.imshow',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_text',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_arrow',   nullfunction)
        self.symtable.set_symbol('_plotter.xrfplot',   nullfunction)

        self._larch.writer = sys.stdout = open('_stdout_', 'w')

    def read_stdout(self):
        sys.stdout.flush()
        time.sleep(0.1)
        with open(sys.stdout.name) as inp:
            out = inp.read()
        sys.stdout.close()
        self._larch.writer = sys.stdout = open('_stdout_', 'w')
        return out

    def run(self, text):
        self.input.put(text)
        ret = None
        buff = []
        while len(self.input) > 0:
            block, fname, lineno = self.input.get()
            buff.append(block)
            if not self.input.complete:
                continue
            ret = self._larch.eval("\n".join(buff), fname=fname, lineno=lineno)
            if self._larch.error:
                break
        return ret

    def get_errors(self):
        return self._larch.error

    def get_symbol(self, name):
        return self.symtable.get_symbol(name, create=False)
Example #3
0
class LarchSession(object):
    def __init__(self):
        self._larch = Interpreter()
        self.input = self._larch.input
        #  InputText(prompt='test>', _larch=self._larch)
        self.symtable = self._larch.symtable
        self.symtable.set_symbol('testdir', os.getcwd())
        self.symtable.set_symbol('_plotter.newplot', nullfunction)
        self.symtable.set_symbol('_plotter.plot', nullfunction)
        self.symtable.set_symbol('_plotter.oplot', nullfunction)
        self.symtable.set_symbol('_plotter.imshow', nullfunction)
        self.symtable.set_symbol('_plotter.plot_text', nullfunction)
        self.symtable.set_symbol('_plotter.plot_arrow', nullfunction)
        self.symtable.set_symbol('_plotter.xrfplot', nullfunction)
        self.set_stdout()

    def set_stdout(self, fname='_stdout_'):
        self._outfile = os.path.abspath(fname)

        self._larch.writer = open(self._outfile, 'w')

    def read_stdout(self):
        self._larch.writer.flush()
        t0 = time.time()
        time.sleep(0.1)
        while (not os.path.exists(self._outfile) and (time.time() - t0) < 5.0):
            time.sleep(0.1)

        with open(self._outfile) as inp:
            out = inp.read()
        os.unlink(self._outfile)
        self._larch.writer = open(self._outfile, 'w')
        return out

    def run(self, text):
        return self._larch.eval(text, fname='test', lineno=0)

    def get_errors(self):
        return self._larch.error

    def get_symbol(self, name):
        return self.symtable.get_symbol(name, create=False)
Example #4
0
class LarchSession(object):
    def __init__(self):
        self._larch = Interpreter()
        self.input  = self._larch.input
        #  InputText(prompt='test>', _larch=self._larch)
        self.symtable = self._larch.symtable
        self.symtable.set_symbol('testdir',  os.getcwd())
        self.symtable.set_symbol('_plotter.newplot',  nullfunction)
        self.symtable.set_symbol('_plotter.plot',     nullfunction)
        self.symtable.set_symbol('_plotter.oplot',    nullfunction)
        self.symtable.set_symbol('_plotter.imshow',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_text',   nullfunction)
        self.symtable.set_symbol('_plotter.plot_arrow',   nullfunction)
        self.symtable.set_symbol('_plotter.xrfplot',   nullfunction)
        self.set_stdout()

    def set_stdout(self, fname='_stdout_'):
        # self._outfile = os.path.abspath(fname)
        # self._larch.writer = open(self._outfile, 'w')
        self._larch.writer = StringIO()

    def read_stdout(self):
        self._larch.writer.flush()
        self._larch.writer.seek(0)
        t0 = time.time()
        time.sleep(0.01)

        out = self._larch.writer.read()
        self._larch.writer.close()
        self.set_stdout()
        return out

    def run(self, text):
        return self._larch.eval(text, fname='test', lineno=0)

    def get_errors(self):
        return self._larch.error

    def get_symbol(self, name):
        return self.symtable.get_symbol(name, create=False)
Example #5
0
class LarchSession(object):
    def __init__(self):
        self._larch = Interpreter()
        self.input  = self._larch.input
        #  InputText(prompt='test>', _larch=self._larch)
        self.symtable = self._larch.symtable
        setsym = self.symtable.set_symbol
        setsym('testdir',  os.getcwd())
        setsym('_plotter.no_plotting', True)
        setsym('_plotter.get_display',  nullfunction)
        self.set_stdout()

    def set_stdout(self, fname='_stdout_'):
        # self._outfile = os.path.abspath(fname)
        # self._larch.writer = open(self._outfile, 'w')
        self._larch.writer = StringIO()

    def read_stdout(self):
        self._larch.writer.flush()
        self._larch.writer.seek(0)
        t0 = time.time()
        time.sleep(0.01)

        out = self._larch.writer.read()
        self._larch.writer.close()
        self.set_stdout()
        return out

    def run(self, text):
        return self._larch.eval(text, fname='test', lineno=0)

    def get_errors(self):
        return self._larch.error

    def get_symbol(self, name):
        return self.symtable.get_symbol(name, create=False)
Example #6
0
class XASController():
    """
    class hollding the Larch session and doing the
    processing work for Larch XAS GUI
    """
    config_file = 'xas_viewer.conf'

    def __init__(self, wxparent=None, _larch=None):
        self.wxparent = wxparent
        self.larch = _larch
        if self.larch is None:
            self.larch = Interpreter()
        self.filelist = None
        self.file_groups = OrderedDict()
        self.fit_opts = {}
        self.group = None

        self.groupname = None
        self.report_frame = None
        self.symtable = self.larch.symtable

    def init_larch(self):
        fico = self.get_iconfile()

        _larch = self.larch
        _larch.eval(XASGUI_STARTUP)
        old_config = read_config(self.config_file)

        config = self.make_default_config()
        for sname in config:
            if old_config is not None and sname in old_config:
                val = old_config[sname]
                if isinstance(val, dict):
                    config[sname].update(val)
                else:
                    config[sname] = val

        for key, value in config.items():
            setattr(_larch.symtable._sys.xas_viewer, key, value)
        os.chdir(config['workdir'])

    def make_default_config(self):
        """ default config, probably called on first run of program"""
        config = {'chdir_on_fileopen': True, 'workdir': os.getcwd()}
        return config

    def get_config(self, key, default=None):
        "get configuration setting"
        confgroup = self.larch.symtable._sys.xas_viewer
        return getattr(confgroup, key, default)

    def save_config(self):
        """save configuration"""
        conf = group2dict(self.larch.symtable._sys.xas_viewer)
        conf.pop('__name__')
        save_config(self.config_file, conf)

    def set_workdir(self):
        self.larch.symtable._sys.xas_viewer.workdir = os.getcwd()

    def show_report(self, fitresult, evt=None):
        shown = False
        try:
            self.report_frame.Raise()
            shown = True
        except:
            del self.report_frame
        if not shown:
            self.report_frame = ReportFrame(self.wxparent)

        model_repr = fitresult.model._reprstring(long=True)
        report = fit_report(fitresult,
                            show_correl=True,
                            min_correl=0.25,
                            sort_pars=True)

        self.report_frame.SetFont(Font(FONTSIZE - 1))
        self.report_frame.set_text(report)
        self.report_frame.SetFont(Font(FONTSIZE - 1))
        self.report_frame.Raise()

    def get_iconfile(self):
        larchdir = self.symtable._sys.config.larchdir
        return os.path.join(larchdir, 'icons', ICON_FILE)

    def get_display(self, win=1, stacked=False):
        wintitle = 'Larch XAS Plot Window %i' % win
        # if stacked:
        #     win = 2
        #    wintitle='Larch XAS Plot Window'
        opts = dict(wintitle=wintitle,
                    stacked=stacked,
                    win=win,
                    size=PLOTWIN_SIZE)
        out = self.symtable._plotter.get_display(**opts)
        if win > 1:
            p1 = getattr(self.symtable._plotter, 'plot1', None)
            if p1 is not None:
                try:
                    siz = p1.GetSize()
                    pos = p1.GetPosition()
                    pos[0] += int(siz[0] / 4)
                    pos[1] += int(siz[1] / 4)
                    out.SetSize(pos)
                    if not stacked:
                        out.SetSize(siz)
                except Exception:
                    pass
        return out

    def get_group(self, groupname=None):
        if groupname is None:
            groupname = self.groupname
            if groupname is None:
                return None
        dgroup = getattr(self.symtable, groupname, None)
        if dgroup is None and groupname in self.file_groups:
            groupname = self.file_groups[groupname]
            dgroup = getattr(self.symtable, groupname, None)
        return dgroup

    def merge_groups(self, grouplist, master=None, yarray='mu', outgroup=None):
        """merge groups"""
        cmd = """%s = merge_groups(%s, master=%s,
        xarray='energy', yarray='%s', kind='cubic', trim=True)"""
        glist = "[%s]" % (', '.join(grouplist))
        outgroup = fix_varname(outgroup.lower())
        if outgroup is None:
            outgroup = 'merged'

        outgroup = unique_name(outgroup, self.file_groups, max=1000)

        cmd = cmd % (outgroup, glist, master, yarray)
        self.larch.eval(cmd)

        if master is None:
            master = grouplist[0]
        this = self.get_group(outgroup)
        master = self.get_group(master)
        if not hasattr(this, 'xasnorm_config'):
            this.xasnorm_config = {}
        this.xasnorm_config.update(master.xasnorm_config)
        this.datatype = master.datatype
        this.xdat = 1.0 * this.energy
        this.ydat = 1.0 * getattr(this, yarray)
        this.plot_xlabel = 'energy'
        this.plot_ylabel = yarray
        return outgroup

    def copy_group(self, filename, new_filename=None):
        """copy XAS group (by filename) to new group"""
        groupname = self.file_groups[filename]
        if not hasattr(self.larch.symtable, groupname):
            return

        ogroup = self.get_group(groupname)

        ngroup = Group(datatype=ogroup.datatype, copied_from=groupname)

        for attr in dir(ogroup):
            do_copy = True
            if attr in ('xdat', 'ydat', 'i0', 'data' 'yerr', 'energy', 'mu'):
                val = getattr(ogroup, attr) * 1.0
            elif attr in ('norm', 'flat', 'deriv', 'deconv', 'post_edge',
                          'pre_edge'):
                do_copy = False
            else:
                try:
                    val = copy.deepcopy(getattr(ogroup, attr))
                except ValueError:
                    do_copy = False
            if do_copy:
                setattr(ngroup, attr, val)

        if new_filename is None:
            new_filename = filename + '_1'
        ngroup.filename = unique_name(new_filename, self.file_groups.keys())
        ngroup.groupname = unique_name(groupname, self.file_groups.values())
        setattr(self.larch.symtable, ngroup.groupname, ngroup)
        return ngroup

    def get_cursor(self, win=None):
        """get last cursor from selected window"""
        return last_cursor_pos(win=win, _larch=self.larch)

    def plot_group(self,
                   groupname=None,
                   title=None,
                   plot_yarrays=None,
                   new=True,
                   zoom_out=True,
                   **kws):
        ppanel = self.get_display(stacked=False).panel
        newplot = ppanel.plot
        oplot = ppanel.oplot
        plotcmd = oplot
        viewlims = ppanel.get_viewlimits()
        if new:
            plotcmd = newplot

        dgroup = self.get_group(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
        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'] = (i != narr)

            plotcmd(dgroup.xdat, getattr(dgroup, yaname), **popts)
            plotcmd = oplot

        if plot_extras is not None:
            axes = ppanel.axes
            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'}
                    popts.update(opts)
                    axes.axvline(x, **popts)
        ppanel.canvas.draw()
Example #7
0
class XYFitController():
    """
    class hollding the Larch session and doing the
    processing work for Larch XYFit
    """
    config_file = 'xyfit.conf'
    def __init__(self, wxparent=None, _larch=None):
        self.wxparent = wxparent
        self.larch = _larch
        if self.larch is None:
            self.larch = Interpreter()

        self.filelist = None
        self.file_groups = {}
        self.proc_opts = {}
        self.fit_opts = {}
        self.group = None

        self.groupname = None
        self.report_frame = None
        self.symtable = self.larch.symtable
        self.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp())
        self.symtable.set_symbol('_sys.wx.parent', self)

    def init_larch(self):
        fico = self.get_iconfile()

        _larch = self.larch
        _larch.eval("import xafs_plots")
        _larch.symtable._sys.xyfit = Group()
        config = read_config(self.config_file)
        if (config is None or 'workdir' not in config or
            'data_proc' not in config or 'xas_proc' not in config):
            config = self.make_default_config()

        for key, value in config.items():
            setattr(_larch.symtable._sys.xyfit, key, value)
        os.chdir(config['workdir'])

    def make_default_config(self):
        """ default config, probably called on first run of program"""
        config = {'chdir_on_fileopen': True,
                  'workdir': os.getcwd()}
        config['data_proc'] = dict(xshift=0, xscale=1, yshift=0,
                                   yscale=1, smooth_op='None',
                                   smooth_conv='Lorentzian',
                                   smooth_c0=2, smooth_c1=1,
                                   smooth_sig=1)
        config['xas_proc'] = dict(e0=0, pre1=-200, pre2=-10,
                                  edge_step=0, nnorm=2, norm1=25,
                                  norm2=-10, nvict=1, auto_step=True,
                                  auto_e0=True, show_e0=True,
                                  xas_op='Normalized')
        return config

    def get_config(self, key, default=None):
        "get configuration setting"
        confgroup = self.larch.symtable._sys.xyfit
        return getattr(confgroup, key, default)


    def save_config(self):
        """save configuration"""
        conf = group2dict(self.larch.symtable._sys.xyfit)
        conf.pop('__name__')
        save_config(self.config_file, conf)

    def set_workdir(self):
        self.larch.symtable._sys.xyfit.workdir = os.getcwd()

    def show_report(self, text, evt=None):
        shown = False
        try:
            self.report_frame.Raise()
            shown = True
        except:
            del self.report_frame
        if not shown:
            self.report_frame = ReportFrame(self.wxparent)

        self.report_frame.SetFont(Font(8))
        self.report_frame.set_text(text)
        self.report_frame.SetFont(Font(8))
        self.report_frame.Raise()

    def get_iconfile(self):
        larchdir = self.symtable._sys.config.larchdir
        return os.path.join(larchdir, 'icons', ICON_FILE)

    def get_display(self, stacked=False):
        win = 1
        wintitle='Larch XYFit Array Plot Window'
        if stacked:
            win = 2
            wintitle='Larch XYFit Fit Plot Window'
        opts = dict(wintitle=wintitle, stacked=stacked, win=win)
        out = self.symtable._plotter.get_display(**opts)
        return out

    def get_group(self, groupname):
        if groupname is None:
            groupname = self.groupname
        grp = getattr(self.symtable, groupname, None)
        if not hasattr(grp, 'proc_opts'):
            grp.proc_opts = {}
        return grp

    def get_proc_opts(self, dgroup):
        opts = {}
        opts.update(self.get_config('data_proc', default={}))
        if dgroup.datatype == 'xas':
            opts.update(self.get_config('xas_proc', {}))

        if hasattr(dgroup, 'proc_opts'):
            opts.update(dgroup.proc_opts)
        return opts

    def process(self, dgroup, proc_opts=None):
        if not hasattr(dgroup, 'proc_opts'):
            dgroup.proc_opts = {}

        if 'xscale' not in dgroup.proc_opts:
            dgroup.proc_opts.update(self.get_proc_opts(dgroup))

        if proc_opts is not None:
            dgroup.proc_opts.update(proc_opts)

        opts = {'group': dgroup.groupname}
        opts.update(dgroup.proc_opts)

        # scaling
        cmds = []
        cmds.append("{group:s}.x = {xscale:f}*({group:s}.xdat + {xshift:f})")
        cmds.append("{group:s}.y = {yscale:f}*({group:s}.ydat + {yshift:f})")

        # smoothing
        smop = opts['smooth_op'].lower()
        smcmd = None
        if smop.startswith('box'):
            opts['smooth_c0'] = int(opts['smooth_c0'])
            smcmd = "boxcar({group:s}.y, {smooth_c0:d})"
        elif smop.startswith('savit'):
            opts['smooth_c0'] = int(opts['smooth_c0'])
            opts['smooth_c1'] = int(opts['smooth_c1'])
            smcmd = "savitzky_golay({group:s}.y, {smooth_c0:d}, {smooth_c1:d})"
        elif smop.startswith('conv'):
            cform = str(opts['smooth_conv'].lower())
            smcmd = "smooth({group:s}.x, {group:s}.y, sigma={smooth_sig:f}, form='{smooth_conv:s}')"

        if smcmd is not None:
            cmds.append("{group:s}.y = " + smcmd)

        for cmd in cmds:
            self.larch.eval(cmd.format(**opts))


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

            dgroup.energy = dgroup.x*1.0
            dgroup.mu = dgroup.y*1.0

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

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

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

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

            opts['e0'] = dgroup.e0
            opts['edge_step'] = dgroup.edge_step
            for attr in  ('pre1', 'pre2', 'norm1', 'norm2'):
                opts[attr] = getattr(dgroup.pre_edge_details, attr)
            dgroup.proc_opts.update(opts)


    def get_cursor(self):
        try:
            xval = self.symtable._plotter.plot1_x
            yval = self.symtable._plotter.plot1_y
        except:
            xval, yval = None, None
        return xval, yval

    def plot_group(self, groupname=None, title=None, new=True, **kws):
        ppanel = self.get_display(stacked=False).panel
        newplot = ppanel.plot
        oplot   = ppanel.oplot
        plotcmd = oplot
        if new:
            plotcmd = newplot

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

        if dgroup.datatype == 'xas':
            if ((getattr(dgroup, 'plot_yarrays', None) is None or
                 getattr(dgroup, 'energy', None) is None or
                 getattr(dgroup, 'mu', None) is None)):
                # print("-> Mode.process")
                self.process(dgroup)

        if not hasattr(dgroup, 'x'):
            dgroup.x = dgroup.xdat[:]
        if not hasattr(dgroup, 'y'):
            dgroup.y = dgroup.ydat[:]

        if hasattr(dgroup, 'plot_yarrays'):
            plot_yarrays = dgroup.plot_yarrays
        else:
            plot_yarrays = [(dgroup.y, {}, None)]

        popts = kws
        path, fname = os.path.split(dgroup.filename)
        if not 'label' in popts:
            popts['label'] = 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

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

        popts['title'] = title
        for yarr in plot_yarrays:
            popts.update(yarr[1])
            if popts['label'] is None and yarr[2] is not None:
                popts['label'] = yarr[2]
            plotcmd(dgroup.x, yarr[0], **popts)
            plotcmd = oplot

        if plot_ymarkers is not None:
            axes = ppanel.axes
            for x, y, opts in plot_ymarkers:
                popts = {'marker': 'o', 'markersize': 4,
                         'markerfacecolor': 'red', 'label': '',
                         'markeredgecolor': 'black'}
                popts.update(opts)
                axes.plot([x], [y], **popts)

        ppanel.canvas.draw()
Example #8
0
class XYFitController():
    """
    class hollding the Larch session and doing the
    processing work for Larch XYFit
    """
    def __init__(self, wxparent=None, _larch=None):
        self.wxparent = wxparent
        self.larch = _larch
        if self.larch is None:
            self.larch = Interpreter()

        self.filelist = None
        self.file_groups = {}
        self.proc_opts = {}
        self.fit_opts = {}
        self.group = None
        self.groupname = None
        self.report_frame = None
        self.symtable = self.larch.symtable
        self.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp())
        self.symtable.set_symbol('_sys.wx.parent', self)

    def show_report(self, text, evt=None):
        shown = False
        try:
            self.report_frame.Raise()
            shown = True
        except:
            del self.report_frame
        if not shown:
            self.report_frame = ReportFrame(self.wxparent)

        self.report_frame.SetFont(Font(8))
        self.report_frame.set_text(text)
        self.report_frame.SetFont(Font(8))
        self.report_frame.Raise()

    def get_iconfile(self):
        larchdir = self.symtable._sys.config.larchdir
        return os.path.join(larchdir, 'icons', ICON_FILE)

    def get_display(self, stacked=False):
        win = 1
        wintitle = 'Larch XYFit Array Plot Window'
        if stacked:
            win = 2
            wintitle = 'Larch XYFit Fit Plot Window'
        opts = dict(wintitle=wintitle, stacked=stacked, win=win)
        out = self.symtable._plotter.get_display(**opts)
        return out

    def get_group(self, groupname):
        if groupname is None:
            groupname = self.groupname
        grp = getattr(self.symtable, groupname, None)
        if not hasattr(grp, 'proc_opts'):
            grp.proc_opts = {}
        return grp

    def get_proc_opts(self, dgroup):
        opts = dict(xshift=0,
                    xscale=1,
                    yshift=0,
                    yscale=1,
                    smooth_op='None',
                    smooth_conv='Lorentzian',
                    smooth_c0=2,
                    smooth_c1=1,
                    smooth_sig=1)

        if dgroup.datatype == 'xas':
            opts.update(
                dict(e0=0,
                     pre1=-200,
                     pre2=-30,
                     edge_step=0,
                     nnorm=3,
                     norm1=50,
                     norm2=-10,
                     nvict=2,
                     auto_step=True,
                     auto_e0=True,
                     show_e0=True,
                     xas_op='Raw Data'))

        if hasattr(dgroup, 'proc_opts'):
            opts.update(dgroup.proc_opts)
        return opts

    def process(self, dgroup, proc_opts=None):
        if not hasattr(dgroup, 'proc_opts'):
            dgroup.proc_opts = {}

        if 'xscale' not in dgroup.proc_opts:
            dgroup.proc_opts.update(self.get_proc_opts(dgroup))

        if proc_opts is not None:
            dgroup.proc_opts.update(proc_opts)

        opts = {'group': dgroup.groupname}
        opts.update(dgroup.proc_opts)

        # scaling
        cmds = [
            "{group:s}.x = {xscale:f}*({group:s}.xdat + {xshift:f})",
            "{group:s}.y = {yscale:f}*({group:s}.ydat + {yshift:f})"
        ]

        # smoothing
        smop = opts['smooth_op'].lower()
        smcmd = None
        if smop.startswith('box'):
            opts['smooth_c0'] = int(opts['smooth_c0'])
            smcmd = "boxcar({group:s}.y, {smooth_c0:d})"
        elif smop.startswith('savit'):
            opts['smooth_c0'] = int(opts['smooth_c0'])
            opts['smooth_c1'] = int(opts['smooth_c1'])
            smcmd = "savitzky_golay({group:s}.y, {smooth_c0:d}, {smooth_c1:d})"
        elif smop.startswith('conv'):
            cform = str(opts['smooth_conv'].lower())
            smcmd = "smooth({group:s}.x, {group:s}.y, sigma={smooth_sig:f}, form='{smooth_conv:s}')"

        if smcmd is not None:
            cmds.append("{group:s}.y = " + smcmd)

        for cmd in cmds:
            self.larch.eval(cmd.format(**opts))

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

            dgroup.energy = dgroup.x
            dgroup.mu = dgroup.y

            popts = dict(group=dgroup.groupname,
                         e0='None',
                         step='None',
                         make_flat='False')

            if not opts['auto_e0']:
                _e0 = opts['e0']
                if _e0 < max(dgroup.energy) and _e0 > min(dgroup.energy):
                    popts['e0'] = "%.f" % float(_e0)

            if not opts['auto_step']:
                popts['step'] = "%.f" % opts['step']

            for attr in ('pre1', 'pre2', 'nvict', 'nnorm', 'norm1', 'norm2'):
                popts[attr] = "%.f" % opts[attr]

            cmd = """pre_edge({group:s}, e0={e0:s}, step={step:s}, pre1={pre1:s}, pre2={pre2:s},
         norm1={norm1:s}, norm2={norm2:s}, nnorm={nnorm:s}, nvict={nvict:s})""".format(
                **popts)

            self.larch.eval(cmd)

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

            for attr in ('pre1', 'pre2', 'norm1', 'norm2'):
                opts[attr] = getattr(dgroup.pre_edge_details, attr)
            dgroup.proc_opts.update(opts)

    def get_cursor(self):
        try:
            xval = self.symtable._plotter.plot1_x
            yval = self.symtable._plotter.plot1_y
        except:
            xval, yval = None, None
        return xval, yval

    def plot_group(self, groupname=None, title=None, new=True, **kws):
        ppanel = self.get_display(stacked=False).panel
        newplot = ppanel.plot
        oplot = ppanel.oplot
        plotcmd = oplot
        if new:
            plotcmd = newplot

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

        if dgroup.datatype == 'xas':
            if ((getattr(dgroup, 'plot_yarrays', None) is None
                 or getattr(dgroup, 'energy', None) is None
                 or getattr(dgroup, 'mu', None) is None)):
                # print("-> Mode.process")
                self.process(dgroup)

        if not hasattr(dgroup, 'x'):
            dgroup.x = dgroup.xdat[:]
        if not hasattr(dgroup, 'y'):
            dgroup.y = dgroup.ydat[:]

        if hasattr(dgroup, 'plot_yarrays'):
            plot_yarrays = dgroup.plot_yarrays
        else:
            plot_yarrays = [(dgroup.y, {}, None)]

        popts = kws
        path, fname = os.path.split(dgroup.filename)
        if not 'label' in popts:
            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.x, yarr[0], **popts)
            plotcmd = oplot

        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()
Example #9
0
class LarchServer(SimpleXMLRPCServer):
    def __init__(self,
                 host='localhost',
                 port=4966,
                 logRequests=False,
                 allow_none=True,
                 keepalive_time=3 * 24 * 3600):
        self.out_buffer = []

        self.larch = Interpreter(writer=self)
        self.input = InputText(prompt='', _larch=self.larch)
        self.larch.run_init_scripts()

        self.larch('_sys.client = group(keepalive_time=%f)' % keepalive_time)
        self.larch('_sys.wx = group(wxapp=None)')
        _sys = self.larch.symtable._sys
        _sys.color_exceptions = False
        _sys.client.last_event = int(time())
        _sys.client.pid_server = int(os.getpid())
        _sys.client.app = 'unknown'
        _sys.client.pid = 0
        _sys.client.user = '******'
        _sys.client.machine = 'unknown'

        self.client = self.larch.symtable._sys.client

        SimpleXMLRPCServer.__init__(self, (host, port),
                                    logRequests=logRequests,
                                    allow_none=allow_none)

        self.register_introspection_functions()
        self.register_function(self.larch_exec, 'larch')
        for method in ('ls', 'chdir', 'cd', 'cwd', 'shutdown',
                       'set_keepalive_time', 'set_client_info',
                       'get_client_info', 'get_data', 'get_rawdata',
                       'get_messages', 'len_messages'):
            self.register_function(getattr(self, method), method)

        # sys.stdout = self
        self.finished = False
        signal.signal(signal.SIGINT, self.signal_handler)
        self.activity_thread = Thread(target=self.check_activity)

    def write(self, text, **kws):
        self.out_buffer.append(text)

    def flush(self):
        pass

    def set_keepalive_time(self, keepalive_time):
        """set keepalive time
        the server will self destruct after keepalive_time of inactivity

        Arguments:
            keepalive_time (number): time in seconds

        """
        self.larch("_sys.client.keepalive_time = %f" % keepalive_time)

    def set_client_info(self, key, value):
        """set client info

        Arguments:
            key (str): category
            value (str): value to use

        Notes:
            the key can actually be any string but include by convention:
               app      application name
               user     user name
               machine  machine name
               pid      process id
        """
        self.larch("_sys.client.%s = '%s'" % (key, value))

    def get_client_info(self):
        """get client info:
        returns json dictionary of client information
        """
        out = {}
        client = self.larch.symtable._sys.client
        for attr in dir(client):
            out[attr] = getattr(client, attr)
        return encode4js(out)

    def get_messages(self):
        """get (and clear) all output messages (say, from "print()")
        """
        out = "".join(self.out_buffer)
        self.out_buffer = []
        return out

    def len_messages(self):
        "length of message buffer"
        return len(self.out_buffer)

    def ls(self, dir_name):
        """list contents of a directory: """
        return os.listdir(dir_name)

    def chdir(self, dir_name):
        """change directory"""
        return os.chdir(dir_name)

    def cd(self, dir_name):
        """change directory"""
        return os.chdir(dir_name)

    def cwd(self):
        """change directory"""
        ret = os.getcwd()
        if sys.platform == 'win32':
            ret = ret.replace('\\', '/')
        return ret

    def signal_handler(self, sig=0, frame=None):
        self.kill()

    def kill(self):
        """handle alarm signal, generated by signal.alarm(t)"""
        sleep(2.0)
        self.shutdown()
        self.server_close()

    def shutdown(self):
        "shutdown LarchServer"
        self.finished = True
        if self.activity_thread.is_alive():
            self.activity_thread.join(POLL_TIME)
        return 1

    def check_activity(self):
        while not self.finished:
            sleep(POLL_TIME)
            # print("Tick ", time()- (self.client.keepalive_time + self.client.last_event))
            if time() > (self.client.keepalive_time + self.client.last_event):
                t = Thread(target=self.kill)
                t.start()
                break

    def larch_exec(self, text):
        "execute larch command"
        text = text.strip()
        if text in ('quit', 'exit', 'EOF'):
            self.shutdown()
        else:
            self.input.put(text, lineno=0)
            if self.input.complete:
                self.larch('_sys.client.last_event = %i' % time())
                self.input.run()
            self.flush()
        return 1

    def get_rawdata(self, expr):
        "return non-json encoded data for a larch expression"
        return self.larch.eval(expr)

    def get_data(self, expr):
        "return json encoded data for a larch expression"
        self.larch('_sys.client.last_event = %i' % time())
        return encode4js(self.larch.eval(expr))

    def run(self):
        """run server until times out"""
        self.activity_thread.start()
        while not self.finished:
            try:
                self.handle_request()
            except:
                break
Example #10
0
class TestCase(unittest.TestCase):
    """testing of asteval"""
    def setUp(self):
        self.interp = Interpreter()
        self.symtable = self.interp.symtable

        self.interp.eval('x = 1')
        self.outfile = '_stdout_'
        self.outfile = '_stdout_'

        self.interp.writer = NamedTemporaryFile('w',
                                                delete=False,
                                                prefix='larchtest')

        if not HAS_NUMPY:
            self.interp("arange = range")

    def read_stdout(self):
        time.sleep(0.1)
        self.interp.writer.flush()
        out = None
        with open(self.interp.writer.name) as inp:
            out = inp.read()
        return out

    def tearDown(self):
        pass

    # noinspection PyUnresolvedReferences
    def isvalue(self, sym, val):
        """assert that a symboltable symbol has a particular value"""
        tval = self.interp.symtable.get_symbol(sym)
        if isinstance(val, np.ndarray):
            return self.assertTrue(np.all(tval == val))
        else:
            return self.assertTrue(tval == val)

    def isnear(self, expr, val, places=7):
        """assert that a symboltable symbol is near a particular value"""
        oval = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            for x, y in zip(oval, val):
                self.assertAlmostEqual(x, y, places=places)
        else:
            return self.assertAlmostEqual(oval, val, places=places)

    # noinspection PyUnresolvedReferences
    def istrue(self, expr):
        """assert that an expression evaluates to True"""
        val = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            val = np.all(val)
        return self.assertTrue(val)

    # noinspection PyUnresolvedReferences
    def isfalse(self, expr):
        """assert that an expression evaluates to False"""
        val = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            val = np.all(val)
        return self.assertFalse(val)

    def check_output(self, chk_str, exact=False):
        self.interp.writer.flush()
        out = self.read_stdout().split('\n')
        if out:
            if exact:
                return chk_str == out[0]
            return chk_str in out[0]
        return False

    def check_error(self, chk_type, chk_msg=''):
        #trye
        if chk_type is None:
            self.assertEqual(0, len(self.interp.error))
        else:
            errtype, errmsg = self.interp.error[0].get_error()
            self.assertEqual(errtype, chk_type)
            if chk_msg:
                self.assertTrue(chk_msg in errmsg)
Example #11
0
class LarchServer(SimpleXMLRPCServer):
    def __init__(self, host='localhost', port=4966,
                 logRequests=False, allow_none=True,
                 keepalive_time=3*24*3600):
        self.out_buffer = []

        self.larch = Interpreter(writer=self)
        self.input = InputText(prompt='', _larch=self.larch)
        self.larch.run_init_scripts()

        self.larch('_sys.client = group(keepalive_time=%f)' % keepalive_time)
        self.larch('_sys.wx = group(wxapp=None)')
        _sys = self.larch.symtable._sys
        _sys.color_exceptions = False
        _sys.client.last_event = int(time())
        _sys.client.pid_server = int(os.getpid())
        _sys.client.app = 'unknown'
        _sys.client.pid = 0
        _sys.client.user = '******'
        _sys.client.machine = 'unknown'

        self.client = self.larch.symtable._sys.client

        SimpleXMLRPCServer.__init__(self, (host, port),
                                    logRequests=logRequests,
                                    allow_none=allow_none)

        self.register_introspection_functions()
        self.register_function(self.larch_exec, 'larch')

        for method in ('ls', 'chdir', 'cd', 'cwd', 'shutdown',
                        'set_keepalive_time', 'set_client_info',
                        'get_client_info', 'get_data', 'get_rawdata',
                        'get_messages', 'len_messages'):
            self.register_function(getattr(self, method), method)

        # sys.stdout = self
        self.finished = False
        signal.signal(signal.SIGINT, self.signal_handler)
        self.activity_thread = Thread(target=self.check_activity)

    def write(self, text, **kws):
        if text is None:
            text = ''
        self.out_buffer.append(str(text))

    def flush(self):
        pass

    def set_keepalive_time(self, keepalive_time):
        """set keepalive time
        the server will self destruct after keepalive_time of inactivity

        Arguments:
            keepalive_time (number): time in seconds

        """
        self.larch("_sys.client.keepalive_time = %f" % keepalive_time)

    def set_client_info(self, key, value):
        """set client info

        Arguments:
            key (str): category
            value (str): value to use

        Notes:
            the key can actually be any string but include by convention:
               app      application name
               user     user name
               machine  machine name
               pid      process id
        """
        self.larch("_sys.client.%s = '%s'" % (key, value))

    def get_client_info(self):
        """get client info:
        returns json dictionary of client information
        """
        out = {}
        client = self.larch.symtable._sys.client
        for attr in dir(client):
            out[attr] = getattr(client, attr)
        return encode4js(out)

    def get_messages(self):
        """get (and clear) all output messages (say, from "print()")
        """
        out = "".join(self.out_buffer)
        self.out_buffer = []
        return out

    def len_messages(self):
        "length of message buffer"
        return len(self.out_buffer)

    def ls(self, dir_name):
        """list contents of a directory: """
        return os.listdir(dir_name)

    def chdir(self, dir_name):
        """change directory"""
        return os.chdir(dir_name)

    def cd(self, dir_name):
        """change directory"""
        return os.chdir(dir_name)

    def cwd(self):
        """change directory"""
        ret = os.getcwd()
        if sys.platform == 'win32':
            ret = ret.replace('\\','/')
        return ret

    def signal_handler(self, sig=0, frame=None):
        self.kill()

    def kill(self):
        """handle alarm signal, generated by signal.alarm(t)"""
        sleep(2.0)
        self.shutdown()
        self.server_close()

    def shutdown(self):
        "shutdown LarchServer"
        self.finished = True
        if self.activity_thread.is_alive():
            self.activity_thread.join(POLL_TIME)
        return 1

    def check_activity(self):
        while not self.finished:
            sleep(POLL_TIME)
            # print("Tick ", time()- (self.client.keepalive_time + self.client.last_event))
            if time() > (self.client.keepalive_time + self.client.last_event):
                t = Thread(target=self.kill)
                t.start()
                break

    def larch_exec(self, text):
        "execute larch command"
        text = text.strip()
        if text in ('quit', 'exit', 'EOF'):
            self.shutdown()
        else:
            self.input.put(text, lineno=0)
            if self.input.complete:
                self.larch('_sys.client.last_event = %i' % time())
                self.input.run()
            self.flush()
        return 1

    def get_rawdata(self, expr):
        "return non-json encoded data for a larch expression"
        return self.larch.eval(expr)

    def get_data(self, expr):
        "return json encoded data for a larch expression"
        self.larch('_sys.client.last_event = %i' % time())
        return encode4js(self.larch.eval(expr))

    def run(self):
        """run server until times out"""
        self.activity_thread.start()
        while not self.finished:
            try:
                self.handle_request()
            except:
                break
Example #12
0
class TestCase(unittest.TestCase):
    """testing of asteval"""

    def setUp(self):
        self.interp = Interpreter()
        self.symtable = self.interp.symtable

        self.interp.eval('x = 1')
        self.outfile = '_stdout_'
        self.outfile = '_stdout_'

        self.interp.writer = NamedTemporaryFile('w', delete=False,
                                                prefix='larchtest')

        if not HAS_NUMPY:
            self.interp("arange = range")

    def read_stdout(self):
        time.sleep(0.1)
        self.interp.writer.flush()
        out = None
        with open(self.interp.writer.name) as inp:
            out = inp.read()
        return out

    def tearDown(self):
        pass


    # noinspection PyUnresolvedReferences
    def isvalue(self, sym, val):
        """assert that a symboltable symbol has a particular value"""
        tval = self.interp.symtable.get_symbol(sym)
        if isinstance(val, np.ndarray):
            return self.assertTrue(np.all(tval == val))
        else:
            return self.assertTrue(tval == val)

    def isnear(self, expr, val, places=7):
        """assert that a symboltable symbol is near a particular value"""
        oval = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            for x, y in zip(oval, val):
                self.assertAlmostEqual(x, y, places=places)
        else:
            return self.assertAlmostEqual(oval, val, places=places)

    # noinspection PyUnresolvedReferences
    def istrue(self, expr):
        """assert that an expression evaluates to True"""
        val = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            val = np.all(val)
        return self.assertTrue(val)

    # noinspection PyUnresolvedReferences
    def isfalse(self, expr):
        """assert that an expression evaluates to False"""
        val = self.interp(expr)
        if HAS_NUMPY and isinstance(val, np.ndarray):
            val = np.all(val)
        return self.assertFalse(val)

    def check_output(self, chk_str, exact=False):
        self.interp.writer.flush()
        out = self.read_stdout().split('\n')
        if out:
            if exact:
                return chk_str == out[0]
            return chk_str in out[0]
        return False

    def check_error(self, chk_type, chk_msg=''):
        #trye
        if chk_type is None:
            self.assertEqual(0, len(self.interp.error))
        else:
            errtype, errmsg = self.interp.error[0].get_error()
            self.assertEqual(errtype, chk_type)
            if chk_msg:
                self.assertTrue(chk_msg in errmsg)
Example #13
0
class SpykShell():
    ps1 = 'Spyk >'
    ps2 = ' ... >'
    def __init__(self, wxparent=None,   writer=None, _larch=None,
                 prompt=None, historyfile=None, output=None, input=None):
        self._larch = _larch
        self.textstyle = None

        if _larch is None:
            self._larch  = Interpreter(historyfile=historyfile,
                                       writer=self)
            self._larch.run_init_scripts()

        self.symtable = self._larch.symtable
        self.prompt = prompt
        self.input  = input
        self.output = output

        self.set_textstyle(mode='text')
        self._larch("_sys.display.colors['text2'] = {'color': 'blue'}",
                    add_history=False)

        self._larch.add_plugin('wx', wxparent=wxparent)
        self.symtable.set_symbol('_builtin.force_wxupdate', False)
        self.symtable.set_symbol('_sys.wx.force_wxupdate', False)
        self.symtable.set_symbol('_sys.wx.wxapp', output)
        self.symtable.set_symbol('_sys.wx.parent', wx.GetApp().GetTopWindow())

        if self.output is not None:
            style = self.output.GetDefaultStyle()
            bgcol = style.GetBackgroundColour()
            sfont = style.GetFont()
            self.textstyle = wx.TextAttr('black', bgcol, sfont)

        self.SetPrompt(True)

        self.flush_timer = wx.Timer(wxparent)
        self.needs_flush = True
        wxparent.Bind(wx.EVT_TIMER, self.onFlushTimer, self.flush_timer)
        self.flush_timer.Start(500)

    def onUpdate(self, event=None):
        symtable = self.symtable
        if symtable.get_symbol('_builtin.force_wxupdate', create=True):
            app = wx.GetApp()
            evtloop = wx.EventLoop()
            while evtloop.Pending():
                evtloop.Dispatch()
            app.ProcessIdle()
        symtable.set_symbol('_builtin.force_wxupdate', False)


    def SetPrompt(self, complete):
        if self.prompt is None:
            return
        sprompt, scolor = self.ps1, '#000075'
        if not complete:
            sprompt, scolor = self.ps2, '#E00075'
        self.prompt.SetLabel(sprompt)
        self.prompt.SetForegroundColour(scolor)
        self.prompt.Refresh()

    def set_textstyle(self, mode='text'):
        if self.output is None:
            return

        display_colors = self._larch.symtable._sys.display.colors
        textattrs = display_colors.get(mode, {'color':'black'})
        color = textattrs['color']
        style = self.output.GetDefaultStyle()
        bgcol = style.GetBackgroundColour()
        sfont = style.GetFont()
        self.textstyle = wx.TextAttr(color, bgcol, sfont)

    def write(self, text, **kws):
        if self.output is None:
            sys.stdout.write(text)
            sys.stdout.flush()
        else:
            pos0 = self.output.GetLastPosition()
            self.output.WriteText(text)
            pos1 = self.output.GetLastPosition()
            self.output.SetStyle(pos0, pos1, self.textstyle)
            self.needs_flush = True

    def flush(self, *args):
        try:
            self.output.SetInsertionPoint(self.output.GetLastPosition())
        except:
            pass
        self.output.Refresh()
        self.output.Update()
        self.needs_flush = False

    def clear_input(self):
        self._larch.input.clear()
        self.SetPrompt(True)

    def onFlushTimer(self, event=None):
        if self.needs_flush:
            self.flush()

    def eval(self, text, add_history=True, **kws):
        if text is None:
            return
        if text.startswith('!'):
            return os.system(text[1:])

        else:
            if add_history:
                self.input.AddToHistory(text)
                self.write(">%s\n" % text)
            ret = self._larch.eval(text, add_history=add_history)
            if self._larch.error:
                self._larch.input.clear()
                self._larch.writer.set_textstyle('error')
                self._larch.show_errors()
                self._larch.writer.set_textstyle('text')
            elif ret is not None:
                self._larch.writer.write("%s\n" % repr(ret))
            self.SetPrompt(self._larch.input.complete)
Example #14
0
class XASController():
    """
    class hollding the Larch session and doing the
    processing work for Larch XAS GUI
    """
    config_file = 'xas_viewer.conf'
    def __init__(self, wxparent=None, _larch=None):
        self.wxparent = wxparent
        self.larch = _larch
        if self.larch is None:
            self.larch = Interpreter()
        self.filelist = None
        self.file_groups = OrderedDict()
        self.fit_opts = {}
        self.group = None

        self.groupname = None
        self.report_frame = None
        self.symtable = self.larch.symtable

    def init_larch(self):
        _larch = self.larch
        old_config = read_config(self.config_file)

        config = self.make_default_config()
        for sname in config:
            if old_config is not None and sname in old_config:
                val = old_config[sname]
                if isinstance(val, dict):
                    config[sname].update(val)
                else:
                    config[sname] = val

        for key, value in config.items():
            setattr(_larch.symtable._sys.xas_viewer, key, value)
        os.chdir(config['workdir'])
        self.wxparent.SetIcon(wx.Icon(self.get_iconfile(), wx.BITMAP_TYPE_ICO))

    def make_default_config(self):
        """ default config, probably called on first run of program"""
        config = {'chdir_on_fileopen': True,
                  'workdir': os.getcwd()}
        return config

    def get_config(self, key, default=None):
        "get configuration setting"
        confgroup = self.larch.symtable._sys.xas_viewer
        return getattr(confgroup, key, default)

    def save_config(self):
        """save configuration"""
        conf = group2dict(self.larch.symtable._sys.xas_viewer)
        conf.pop('__name__')
        save_config(self.config_file, conf)

    def set_workdir(self):
        self.larch.symtable._sys.xas_viewer.workdir = os.getcwd()

    def get_iconfile(self):
        larchdir = self.symtable._sys.config.larchdir
        return os.path.join(larchdir, 'icons', ICON_FILE)

    def get_display(self, win=1, stacked=False):
        wintitle='Larch XAS Plot Window %i' % win
        # if stacked:
        #     win = 2
        #    wintitle='Larch XAS Plot Window'
        opts = dict(wintitle=wintitle, stacked=stacked, win=win,
                    size=PLOTWIN_SIZE)
        out = self.symtable._plotter.get_display(**opts)
        if win > 1:
            p1 = getattr(self.symtable._plotter, 'plot1', None)
            if p1 is not None:
                try:
                    siz = p1.GetSize()
                    pos = p1.GetPosition()
                    pos[0] += int(siz[0]/4)
                    pos[1] += int(siz[1]/4)
                    out.SetSize(pos)
                    if not stacked:
                        out.SetSize(siz)
                except Exception:
                    pass
        return out

    def get_group(self, groupname=None):
        if groupname is None:
            groupname = self.groupname
            if groupname is None:
                return None
        dgroup = getattr(self.symtable, groupname, None)
        if dgroup is None and groupname in self.file_groups:
            groupname = self.file_groups[groupname]
            dgroup = getattr(self.symtable, groupname, None)
        return dgroup

    def filename2group(self, filename):
        "convert filename (as displayed) to larch group"
        return self.get_group(self.file_groups[str(filename)])

    def merge_groups(self, grouplist, master=None, yarray='mu', outgroup=None):
        """merge groups"""
        cmd = """%s = merge_groups(%s, master=%s,
        xarray='energy', yarray='%s', kind='cubic', trim=True)"""
        glist = "[%s]" % (', '.join(grouplist))
        outgroup = fix_varname(outgroup.lower())
        if outgroup is None:
            outgroup = 'merged'

        outgroup = unique_name(outgroup, self.file_groups, max=1000)

        cmd = cmd % (outgroup, glist, master, yarray)
        self.larch.eval(cmd)

        if master is None:
            master = grouplist[0]
        this = self.get_group(outgroup)
        master = self.get_group(master)
        if not hasattr(this, 'xasnorm_config'):
            this.xasnorm_config = {}
        this.xasnorm_config.update(master.xasnorm_config)
        this.datatype = master.datatype
        this.xdat = 1.0*this.energy
        this.ydat = 1.0*getattr(this, yarray)
        this.yerr =  getattr(this, 'd' + yarray, 1.0)
        if yarray != 'mu':
            this.mu = this.ydat
        this.plot_xlabel = 'energy'
        this.plot_ylabel = yarray
        return outgroup

    def copy_group(self, filename, new_filename=None):
        """copy XAS group (by filename) to new group"""
        groupname = self.file_groups[filename]
        if not hasattr(self.larch.symtable, groupname):
            return

        ogroup = self.get_group(groupname)

        ngroup = Group(datatype=ogroup.datatype,
                       copied_from=groupname)

        for attr in dir(ogroup):
            do_copy = True
            if attr in ('xdat', 'ydat', 'i0', 'data' 'yerr',
                        'energy', 'mu'):
                val = getattr(ogroup, attr)*1.0
            elif attr in ('norm', 'flat', 'deriv', 'deconv',
                          'post_edge', 'pre_edge', 'norm_mback',
                          'norm_vict', 'norm_poly'):
                do_copy = False
            else:
                try:
                    val = copy.deepcopy(getattr(ogroup, attr))
                except ValueError:
                    do_copy = False
            if do_copy:
                setattr(ngroup, attr, val)

        if new_filename is None:
            new_filename = filename + '_1'
        ngroup.filename = unique_name(new_filename, self.file_groups.keys())
        ngroup.groupname = unique_name(groupname, self.file_groups.values())
        setattr(self.larch.symtable, ngroup.groupname, ngroup)
        return ngroup

    def get_cursor(self, win=None):
        """get last cursor from selected window"""
        return last_cursor_pos(win=win, _larch=self.larch)

    def plot_group(self, groupname=None, title=None, plot_yarrays=None,
                   new=True, zoom_out=True, **kws):
        ppanel = self.get_display(stacked=False).panel
        newplot = ppanel.plot
        oplot   = ppanel.oplot
        plotcmd = oplot
        viewlims = ppanel.get_viewlimits()
        if new:
            plotcmd = newplot

        dgroup = self.get_group(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
        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'] = (i != narr)

            plotcmd(dgroup.xdat, getattr(dgroup, yaname), **popts)
            plotcmd = oplot

        if plot_extras is not None:
            axes = ppanel.axes
            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'}
                    popts.update(opts)
                    axes.axvline(x, **popts)
        ppanel.canvas.draw()