コード例 #1
0
ファイル: xyfit.py プロジェクト: experimentAccount5/xraylarch
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()
コード例 #2
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()
コード例 #3
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()