Ejemplo n.º 1
0
 def config_xraylines(self, event=None):
     """show configuration frame"""
     try:
         self.win_config.Raise()
     except:
         self.win_config = XrayLinesFrame(parent=self)
Ejemplo n.º 2
0
class XRFDisplayFrame(wx.Frame):
    _about = """XRF Spectral Viewer
  Matt Newville <newville @ cars.uchicago.edu>
  """
    main_title = 'XRF Display'
    def __init__(self, _larch=None, parent=None, mca_file=None,
                 size=(725, 450), axissize=None, axisbg=None,
                 title='XRF Display', exit_callback=None,
                 output_title='XRF', **kws):

        if size is None: size = (725, 450)
        wx.Frame.__init__(self, parent=parent,
                          title=title, size=size,  **kws)
        self.conf = XRFDisplayConfig()

        self.subframes = {}
        self.data = None
        self.title = title
        self.plotframe = None
        self.wids = {}
        self.larch = _larch
        if self.larch is None:
            self.init_larch()
        self._mcagroup = self.larch.symtable.new_group('_mcas')
        self.exit_callback = exit_callback
        self.roi_patch = None
        self.selected_roi = None
        self.roilist_sel  = None
        self.selected_elem = None
        self.mca = None
        self.mca2 = None
        self.xdata = np.arange(2048)*0.015
        self.ydata = np.ones(2048)*1.e-4
        self.x2data = None
        self.y2data = None
        self.rois_shown = False
        self.major_markers = []
        self.minor_markers = []
        self.hold_markers = []

        self.hold_lines = None
        self.saved_lines = None
        self.energy_for_zoom = None
        self.xview_range = None
        self.show_yaxis = False
        self.xmarker_left = None
        self.xmarker_right = None

        self.highlight_xrayline = None
        self.highlight_xrayline = None
        self.cursor_markers = [None, None]
        self.ylog_scale = True
        self.SetTitle("%s: %s " % (self.main_title, title))

        self._menus = []
        self.createMainPanel()
        self.createMenus()
        self.SetFont(Font(9, serif=True))
        self.statusbar = self.CreateStatusBar(4)
        self.statusbar.SetStatusWidths([-1, -1, -1, -1])
        statusbar_fields = ["XRF Display", " ", " ", " "]
        for i in range(len(statusbar_fields)):
            self.statusbar.SetStatusText(statusbar_fields[i], i)
        if mca_file is not None:
            self.mca = gsemca_group(mca_file, _larch=self.larch)
            self._mcagroup.mca1 = self.mca
            self._mcagroup.mca2 = None
            self.plotmca(self.mca, show_mca2=False)

    def ignoreEvent(self, event=None):
        pass

    def on_cursor(self, event=None, side='left'):
        if event is None:
            return
        x, y  = event.xdata, event.ydata
        if len(self.panel.fig.get_axes()) > 1:
            try:
                x, y = self.panel.axes.transData.inverted().transform((event.x, event.y))
            except:
                pass
        ix = x
        if self.mca is not None:
            ix = index_of(self.mca.energy, x)

        if side == 'right':
            self.xmarker_right = ix
        elif side == 'left':
            self.xmarker_left = ix

        if self.xmarker_left is not None and self.xmarker_right is not None:
            ix1, ix2 = self.xmarker_left, self.xmarker_right
            self.xmarker_left  = min(ix1, ix2)
            self.xmarker_right = max(ix1, ix2)

        if side == 'left':
            self.energy_for_zoom = self.mca.energy[ix]
        self.update_status()
        self.draw()

    def clear_lines(self, evt=None):
        "remove all Line Markers"
        for m in self.major_markers + self.minor_markers + self.hold_markers:
            try:
                m.remove()
            except:
                pass
        if self.highlight_xrayline is not None:
            try:
                self.highlight_xrayline.remove()
            except:
                pass

        self.highlight_xrayline = None
        self.major_markers = []
        self.minor_markers = []
        self.hold_markers = []
        self.draw()

    def draw(self):
        try:
            self.panel.canvas.draw()
        except:
            pass

    def clear_markers(self, evt=None):
        "remove all Cursor Markers"
        for m in self.cursor_markers:
            if m is not None:
                m.remove()
        self.cursor_markers = [None, None]
        self.xmarker_left  = None
        self.xmarker_right = None
        self.draw()

    def clear_background(self, evt=None):
        "remove XRF background"
        self.mca2 = None
        self.plotmca(self.mca)

    def update_status(self):
        fmt = "{:s}: Chan={:} En={:.3f}  Counts={:,.0f}".format
        if (self.xmarker_left is None and
            self.xmarker_right is None and
            self.selected_roi is None):
            return

        log = np.log10
        axes= self.panel.axes
        def draw_ymarker_range(idx, x, y):
            ymin, ymax = self.panel.axes.get_ylim()
            y1 = (y-ymin)/(ymax-ymin+0.0002)
            if y < 1.0: y = 1.0
            if  self.ylog_scale:
                y1 = (log(y)-log(ymin))/(log(ymax)-log(ymin)+2.e-9)
                if y1 < 0.0: y1 = 0.0
            y2 = min(y1+0.25, y1*0.1 + 0.9)
            if self.cursor_markers[idx] is not None:
                try:
                    self.cursor_markers[idx].remove()
                except:
                    pass
            self.cursor_markers[idx] = axes.axvline(x, y1, y2, linewidth=2.0,
                                                    color=self.conf.marker_color)

        if self.xmarker_left is not None:
            ix = self.xmarker_left
            x, y = self.xdata[ix],  self.ydata[ix]
            draw_ymarker_range(0, x, y)
            self.write_message(fmt("L", ix, x, y), panel=1)
        if self.xmarker_right is not None:
            ix = self.xmarker_right
            x, y = self.xdata[ix],  self.ydata[ix]
            draw_ymarker_range(1, x, y)
            self.write_message(fmt("R", ix, x, y), panel=2)

        if self.mca is None:
            return

        if (self.xmarker_left is not None and
            self.xmarker_right is not None):
            self.ShowROIStatus(self.xmarker_left,
                               self.xmarker_right,
                               name='', panel=3)

        if self.selected_roi is not None:
            roi = self.selected_roi
            left, right = roi.left, roi.right
            self.ShowROIStatus(left, right, name=roi.name, panel=0)
            self.ShowROIPatch(left, right)

    def createPlotPanel(self):
        """mca plot window"""
        pan = PlotPanel(self, fontsize=7,
                        axisbg='#FEFEFE',
                        # axissize=[0.01, 0.11, 0.97, 0.87],
                        output_title='test.xrf',
                        messenger=self.write_message)
        pan.conf.grid_color='#E5E5C0'
        pan.conf.show_grid = False
        pan.conf.canvas.figure.set_facecolor('#FCFCFE')
        pan.conf.labelfont.set_size(6)
        pan.conf.labelfont.set_size(6)
        pan.onRightDown= partial(self.on_cursor, side='right')
        pan.add_cursor_mode('zoom',  motion = self.ignoreEvent,
                            leftup   = self.ignoreEvent,
                            leftdown = self.on_cursor,
                            rightdown = partial(self.on_cursor, side='right'))
        return pan

    def createControlPanel(self):
        ctrlpanel = wx.Panel(self, name='Ctrl Panel')

        ptable = PeriodicTablePanel(ctrlpanel,  onselect=self.onShowLines,
                                    tooltip_msg='Select Element for KLM Lines')

        self.wids['ptable'] = ptable

        labstyle = wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.EXPAND
        ctrlstyle = wx.ALIGN_LEFT|wx.ALIGN_BOTTOM
        txtstyle=wx.ALIGN_LEFT|wx.ST_NO_AUTORESIZE|wx.TE_PROCESS_ENTER
        Font9  = Font(9)
        Font10 = Font(10)
        Font11 = Font(11)
        #
        arrowpanel = wx.Panel(ctrlpanel)
        ssizer = wx.BoxSizer(wx.HORIZONTAL)
        for wname, dname in (('uparrow', 'up'),
                             ('leftarrow', 'left'),
                             ('rightarrow', 'right'),
                             ('downarrow', 'down')):
            self.wids[wname] = wx.BitmapButton(arrowpanel, -1,
                                               get_icon(wname),
                                               size=(25, 25),
                                               style=wx.NO_BORDER)
            self.wids[wname].Bind(wx.EVT_BUTTON,
                                 partial(ptable.onKey, name=dname))

            ssizer.Add(self.wids[wname],  0, wx.EXPAND|wx.ALL)

        self.wids['kseries'] = Check(arrowpanel, ' K ', action=self.onKLM)
        self.wids['lseries'] = Check(arrowpanel, ' L ', action=self.onKLM)
        self.wids['mseries'] = Check(arrowpanel, ' M ', action=self.onKLM)
        self.wids['holdbtn'] = wx.ToggleButton(arrowpanel, -1, 'Hold   ', size=(65, 25))
        self.wids['holdbtn'].Bind(wx.EVT_TOGGLEBUTTON, self.onToggleHold)

        ssizer.Add(txt(arrowpanel, '  '),   0, wx.EXPAND|wx.ALL, 0)
        ssizer.Add(self.wids['holdbtn'],    0, wx.EXPAND|wx.ALL, 2)
        ssizer.Add(self.wids['kseries'],    0, wx.EXPAND|wx.ALL, 0)
        ssizer.Add(self.wids['lseries'],    0, wx.EXPAND|wx.ALL, 0)
        ssizer.Add(self.wids['mseries'],    0, wx.EXPAND|wx.ALL, 0)
        pack(arrowpanel, ssizer)

        # roi section...
        rsizer = wx.GridBagSizer(4, 6)
        roipanel = wx.Panel(ctrlpanel, name='ROI Panel')
        self.wids['roilist'] = wx.ListBox(roipanel, size=(135, 120))
        self.wids['roilist'].Bind(wx.EVT_LISTBOX, self.onROI)
        self.wids['roilist'].SetMinSize((135, 120))
        self.wids['roiname'] = wx.TextCtrl(roipanel, -1, '', size=(150, -1))

        #
        roibtns= wx.Panel(roipanel, name='ROIButtons')
        zsizer = wx.BoxSizer(wx.HORIZONTAL)
        z1 = Button(roibtns, 'Add',    size=(70, 30), action=self.onNewROI)
        z2 = Button(roibtns, 'Delete', size=(70, 30), action=self.onConfirmDelROI)
        z3 = Button(roibtns, 'Rename', size=(70, 30), action=self.onRenameROI)

        zsizer.Add(z1,    0, wx.EXPAND|wx.ALL, 0)
        zsizer.Add(z2,    0, wx.EXPAND|wx.ALL, 0)
        zsizer.Add(z3,    0, wx.EXPAND|wx.ALL, 0)
        pack(roibtns, zsizer)

        rt1 = txt(roipanel, ' Channels:', size=75, font=Font9)
        rt2 = txt(roipanel, ' Energy:',   size=75, font=Font9)
        rt3 = txt(roipanel, ' Cen/Wid:',  size=75, font=Font9)
        m = ''
        self.wids['roi_msg1'] = txt(roipanel, m, size=135, font=Font9)
        self.wids['roi_msg2'] = txt(roipanel, m, size=135, font=Font9)
        self.wids['roi_msg3'] = txt(roipanel, m, size=135, font=Font9)

        rsizer.Add(txt(roipanel, ' Regions of Interest:', size=125, font=Font11),
                   (0, 0), (1, 3), labstyle)
        rsizer.Add(self.wids['roiname'],    (1, 0), (1, 3), labstyle)
        rsizer.Add(roibtns,                 (2, 0), (1, 3), labstyle)
        rsizer.Add(rt1,                     (3, 0), (1, 1), LEFT)
        rsizer.Add(rt2,                     (4, 0), (1, 1), LEFT)
        rsizer.Add(rt3,                     (5, 0), (1, 1), LEFT)
        rsizer.Add(self.wids['roi_msg1'],   (3, 1), (1, 2), labstyle)
        rsizer.Add(self.wids['roi_msg2'],   (4, 1), (1, 2), labstyle)
        rsizer.Add(self.wids['roi_msg3'],   (5, 1), (1, 2), labstyle)
        rsizer.Add(self.wids['roilist'],    (0, 3), (6, 1),
                   wx.EXPAND|wx.ALL|wx.ALIGN_RIGHT)
        rsizer.SetHGap(1)

        pack(roipanel, rsizer)
        # end roi section

        # y scale
        yscalepanel = wx.Panel(ctrlpanel, name='YScalePanel')
        ysizer = wx.BoxSizer(wx.HORIZONTAL)
        ytitle = txt(yscalepanel, ' Y Axis:', font=Font10, size=80)
        yspace = txt(yscalepanel, ' ', font=Font10, size=20)
        ylog   = Choice(yscalepanel, size=(80, 30), choices=['log', 'linear'],
                      action=self.onLogLinear)
        yaxis  = Check(yscalepanel, ' Show Y Scale ', action=self.onYAxis,
                      default=False)
        self.wids['show_yaxis'] = yaxis
        ysizer.Add(ytitle,  0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0)
        ysizer.Add(ylog,    0, wx.EXPAND|wx.ALL, 0)
        ysizer.Add(yspace,  0, wx.EXPAND|wx.ALL, 0)
        ysizer.Add(yaxis,   0, wx.EXPAND|wx.ALL, 0)
        pack(yscalepanel, ysizer)

        # zoom buttons
        zoompanel = wx.Panel(ctrlpanel, name='ZoomPanel')
        zsizer = wx.BoxSizer(wx.HORIZONTAL)
        z1 = Button(zoompanel, 'Zoom In',   size=(80, 30), action=self.onZoomIn)
        z2 = Button(zoompanel, 'Zoom Out',  size=(80, 30), action=self.onZoomOut)
        p1 = Button(zoompanel, 'Pan Lo',    size=(75, 30), action=self.onPanLo)
        p2 = Button(zoompanel, 'Pan Hi',    size=(75, 30), action=self.onPanHi)

        zsizer.Add(p1,      0, wx.EXPAND|wx.ALL, 0)
        zsizer.Add(p2,      0, wx.EXPAND|wx.ALL, 0)
        zsizer.Add(z1,      0, wx.EXPAND|wx.ALL, 0)
        zsizer.Add(z2,      0, wx.EXPAND|wx.ALL, 0)
        pack(zoompanel, zsizer)

        #
        self.wids['xray_lines'] = None
        if HAS_DV:
            dvstyle = dv.DV_SINGLE|dv.DV_VERT_RULES|dv.DV_ROW_LINES
            xlines = dv.DataViewListCtrl(ctrlpanel, style=dvstyle)
            self.wids['xray_lines'] = xlines
            xlines.AppendTextColumn('Line  ',         width=55)
            xlines.AppendTextColumn('Energy(keV)  ',  width=110)
            xlines.AppendTextColumn('Strength  ',     width=85)
            xlines.AppendTextColumn('Levels  ',       width=75)
            for col in (0, 1, 2, 3):
                this = xlines.Columns[col]
                this.Sortable = True
                align = RIGHT
                if col in (0, 3): align = wx.ALIGN_LEFT
                this.Alignment = this.Renderer.Alignment = align

            xlines.SetMinSize((300, 240))
            xlines.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED,
                        self.onSelectXrayLine)
        # main layout
        # may have to adjust comparison....
        store = xlines.GetStore()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(roipanel,            0, labstyle)
        sizer.Add(lin(ctrlpanel, 195), 0, labstyle)
        sizer.Add(yscalepanel,         0, wx.ALIGN_RIGHT|wx.EXPAND|wx.ALL)
        sizer.Add(zoompanel,           0, wx.ALIGN_RIGHT|wx.EXPAND|wx.ALL)
        sizer.Add(lin(ctrlpanel, 195), 0, labstyle)
        sizer.Add(ptable,              0, wx.ALIGN_RIGHT|wx.EXPAND|wx.ALL, 4)
        sizer.Add(arrowpanel,          0, labstyle)
        sizer.Add(lin(ctrlpanel, 195), 0, labstyle)

        if self.wids['xray_lines'] is not None:
            sizer.Add(xlines,  0, wx.ALIGN_CENTER|wx.GROW|wx.ALL|wx.EXPAND)

        pack(ctrlpanel, sizer)
        return ctrlpanel

    def createMainPanel(self):
        ctrlpanel = self.createControlPanel()
        plotpanel = self.panel = self.createPlotPanel()
        plotpanel.yformatter = self._formaty

        tx, ty = self.wids['ptable'].GetBestSize()
        cx, cy = ctrlpanel.GetBestSize()
        px, py = plotpanel.GetBestSize()

        self.SetSize((max(cx, tx)+px, 25+max(cy, py)))

        style = wx.ALIGN_LEFT|wx.EXPAND|wx.ALL
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(ctrlpanel, 0, style, 3)
        sizer.Add(plotpanel, 1, style, 2)

        self.SetMinSize((450, 150))
        pack(self, sizer)
        wx.CallAfter(self.init_larch)
        self.set_roilist(mca=None)

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

        fico = os.path.join(site_config.larchdir, 'icons', ICON_FILE)
        try:
            self.SetIcon(wx.Icon(fico, wx.BITMAP_TYPE_ICO))
        except:
            pass

    def _getlims(self):
        emin, emax = self.panel.axes.get_xlim()
        erange = emax-emin
        emid   = (emax+emin)/2.0
        if self.energy_for_zoom is not None:
            emid = self.energy_for_zoom
        dmin, dmax = emin, emax
        drange = erange
        if self.mca is not None:
            dmin, dmax = self.mca.energy.min(), self.mca.energy.max()
        return (emid, erange, dmin, dmax)

    def _set_xview(self, e1, e2, keep_zoom=False):
        if not keep_zoom:
            self.energy_for_zoom = (e1+e2)/2.0
        self.panel.axes.set_xlim((e1, e2))
        self.xview_range = [e1, e2]
        self.draw()

    def onPanLo(self, event=None):
        emid, erange, dmin, dmax = self._getlims()
        e1 = max(dmin, emid-0.9*erange)
        e2 = min(dmax, e1 + erange)
        self._set_xview(e1, e2)

    def onPanHi(self, event=None):
        emid, erange, dmin, dmax = self._getlims()
        e2 = min(dmax, emid+0.9*erange)
        e1 = max(dmin, e2-erange)
        self._set_xview(e1, e2)

    def onZoomIn(self, event=None):
        emid, erange, dmin, dmax = self._getlims()
        e1 = max(dmin, emid-erange/3.0)
        e2 = min(dmax, emid+erange/3.0)
        self._set_xview(e1, e2, keep_zoom=True)

    def onZoomOut(self, event=None):
        emid, erange, dmin, dmax = self._getlims()
        e1 = max(dmin, emid-1.25*erange)
        e2 = min(dmax, emid+1.25*erange)
        self._set_xview(e1, e2)

    def unzoom_all(self, event=None):
        emid, erange, dmin, dmax = self._getlims()
        self._set_xview(dmin, dmax)
        self.xview_range = None

    def toggle_grid(self, event=None):
        self.panel.toggle_grid()

    def set_roilist(self, mca=None):
        """ Add Roi names to roilist"""
        self.wids['roilist'].Clear()
        if mca is not None:
            for roi in mca.rois:
                name = roi.name.strip()
                if len(name) > 0:
                    self.wids['roilist'].Append(roi.name)

    def clear_roihighlight(self, event=None):
        self.selected_roi = None
        try:
            self.roi_patch.remove()
        except:
            pass
        self.roi_patch = None
        self.wids['roiname'].SetValue('')
        self.draw()

    def get_roiname(self):
        roiname = self.wids['roiname'].GetValue()
        if len(roiname) < 1:
            roiname = 'ROI 1'
            names = [str(r.name.lower()) for r in self.mca.rois]
            if str(roiname.lower()) in names:
                ix = 1
                while str(roiname.lower()) in names:
                    roiname = "ROI %i" % (ix)
                    ix += 1
        return roiname

    def onNewROI(self, event=None):
        if (self.xmarker_left is None or
            self.xmarker_right is None or self.mca is None):
            return
        roiname = self.get_roiname()

        names = [str(r.name.lower()) for r in self.mca.rois]
        if str(roiname.lower()) in names:
            msg = "Overwrite Definition of ROI {:s}?".format(roiname)
            if (wx.ID_YES != Popup(self, msg, 'Overwrite ROI?', style=wx.YES_NO)):
                return False

        left, right  = self.xmarker_left, self.xmarker_right
        if left > right:
            left, right = right, left
        self.mca.add_roi(name=roiname, left=left, right=right, sort=True)
        self.set_roilist(mca=self.mca)
        for roi in self.mca.rois:
            if roi.name.lower()==roiname:
                selected_roi = roi
        self.plot(self.xdata, self.ydata)
        self.onROI(label=roiname)
        if self.selected_elem is not None:
            self.onShowLines(elem=self.selected_elem)
        return True

    def onConfirmDelROI(self, event=None):
        roiname = self.wids['roiname'].GetValue()
        msg = "Delete ROI {:s}?".format(roiname)
        if (wx.ID_YES == Popup(self, msg,   'Delete ROI?', style=wx.YES_NO)):
            self.onDelROI()

    def onRenameROI(self, event=None):
        roiname = self.get_roiname()

        if self.roilist_sel is not None:
            names = self.wids['roilist'].GetStrings()
            names[self.roilist_sel] = roiname
            self.wids['roilist'].Clear()
            for sname in names:
                self.wids['roilist'].Append(sname)
            self.wids['roilist'].SetSelection(self.roilist_sel)

    def onDelROI(self):
        roiname = self.wids['roiname'].GetValue()
        rdat = []
        if self.mca is None:
            return
        for i in range(len(self.mca.rois)):
            roi = self.mca.rois.pop(0)
            if roi.name.lower() != roiname.lower():
                rdat.append((roi.name, roi.left, roi.right))

        for name, left, right in rdat:
            self.mca.add_roi(name=name, left=left, right=right, sort=False)
        self.mca.rois.sort()
        self.set_roilist(mca=self.mca)
        self.wids['roiname'].SetValue('')
        try:
            self.roi_patch.remove()
        except:
            pass

        self.plot(self.xdata, self.ydata)
        if self.selected_elem is not None:
            self.onShowLines(elem=self.selected_elem)

    def ShowROIStatus(self, left, right, name='', panel=0):
        if left > right:
            return
        sum = self.ydata[left:right].sum()
        dt = self.mca.real_time
        nmsg, cmsg, rmsg = '', '', ''
        if len(name) > 0:
            nmsg = " %s" % name
        cmsg = " Counts={:10,.0f}".format(sum)
        if dt is not None and dt > 1.e-9:
            rmsg = " CPS={:10,.1f}".format(sum/dt)
        self.write_message("%s%s%s" % (nmsg, cmsg, rmsg), panel=panel)

    def ShowROIPatch(self, left, right):
        """show colored XRF Patch:
        Note: ROIs larger than half the energy are not colored"""
        # xnpts = 1.0/len(self.mca.energy)
        # if xnpts*(right - left) > 0.5:
        #    return
        try:
            self.roi_patch.remove()
        except:
            pass

        e = np.zeros(right-left+2)
        r = np.ones(right-left+2)
        e[1:-1] = self.mca.energy[left:right]
        r[1:-1] = self.mca.counts[left:right]
        e[0]  = e[1]
        e[-1] = e[-2]
        self.roi_patch = self.panel.axes.fill_between(e, r, zorder=-20,
                                           color=self.conf.roi_fillcolor)

    def onROI(self, event=None, label=None):
        if label is None and event is not None:
            label = event.GetString()
            self.roilist_sel = event.GetSelection()
        self.wids['roiname'].SetValue(label)
        name, left, right= None, -1, -1
        label = label.lower().strip()

        self.selected_roi = None
        if self.mca is not None:
            for roi in self.mca.rois:
                if roi.name.lower()==label:
                    left, right, name = roi.left, roi.right, roi.name
                    elo  = self.mca.energy[left]
                    ehi  = self.mca.energy[right]
                    self.selected_roi = roi
                    break
        if name is None or right == -1:
            return

        self.ShowROIStatus(left, right, name=name)
        self.ShowROIPatch(left, right)

        roi_msg1 = '[{:} : {:}]'.format(left, right)
        roi_msg2 = '[{:6.3f} : {:6.3f}]'.format(elo, ehi)
        roi_msg3 = ' {:6.3f} / {:6.3f} '.format((elo+ehi)/2., (ehi - elo))

        self.energy_for_zoom = (elo+ehi)/2.0

        self.wids['roi_msg1'].SetLabel(roi_msg1)
        self.wids['roi_msg2'].SetLabel(roi_msg2)
        self.wids['roi_msg3'].SetLabel(roi_msg3)

        self.draw()
        self.panel.Refresh()

    def onSaveROIs(self, event=None):
        pass

    def onRestoreROIs(self, event=None):
        pass

    def createCustomMenus(self):
        return

    def createBaseMenus(self):
        fmenu = wx.Menu()
        MenuItem(self, fmenu, "&Read MCA Spectra File\tCtrl+O",
                 "Read GSECARS MCA File",  self.onReadMCAFile)

        fmenu.AppendSeparator()
        MenuItem(self, fmenu, "&Save MCA File\tCtrl+S",
                 "Save GSECARS MCA File",  self.onSaveMCAFile)
        MenuItem(self, fmenu, "&Save ASCII Column File\tCtrl+A",
                 "Save Column File",  self.onSaveColumnFile)

        fmenu.AppendSeparator()
        MenuItem(self, fmenu, "Save ROIs to File",
                 "Save ROIs to File",  self.onSaveROIs)
        MenuItem(self, fmenu, "Restore ROIs File",
                 "Read ROIs from File",  self.onRestoreROIs)

        fmenu.AppendSeparator()
        MenuItem(self, fmenu,  "&Save Plot\tCtrl+I",
                 "Save PNG Image of Plot", self.onSavePNG)
        MenuItem(self, fmenu, "&Copy Plot\tCtrl+C",
                 "Copy Plot Image to Clipboard",
                 self.onCopyImage)
        MenuItem(self, fmenu, 'Page Setup...', 'Printer Setup', self.onPageSetup)
        MenuItem(self, fmenu, 'Print Preview...', 'Print Preview', self.onPrintPreview)
        MenuItem(self, fmenu, "&Print\tCtrl+P", "Print Plot", self.onPrint)

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

        omenu = wx.Menu()
        MenuItem(self, omenu, "Configure Colors",
                 "Configure Colors", self.config_colors)
        MenuItem(self, omenu, "Configure X-ray Lines",
                 "Configure which X-ray Lines are shown", self.config_xraylines)
        MenuItem(self, omenu, "Configure Plot\tCtrl+K",
                 "Configure Plot Colors, etc", self.panel.configure)
        MenuItem(self, omenu, "Zoom Out\tCtrl+Z",
                 "Zoom out to full data range", self.unzoom_all)
        MenuItem(self, omenu, "Toggle Grid\tCtrl+G",
                 "Toggle Grid Display", self.toggle_grid)
        omenu.AppendSeparator()
        MenuItem(self, omenu, "Hide X-ray Lines",
                 "Hide all X-ray Lines", self.clear_lines)
        MenuItem(self, omenu, "Hide selected ROI ",
                 "Hide selected ROI", self.clear_roihighlight)
        MenuItem(self, omenu, "Hide Markers ",
                 "Hide cursor markers", self.clear_markers)
        MenuItem(self, omenu, "Hide XRF Background ",
                 "Hide cursor markers", self.clear_background)

        omenu.AppendSeparator()
        MenuItem(self, omenu, "Swap MCA and Background MCA",
                 "Swap Foreground and Background MCAs", self.swap_mcas)
        MenuItem(self, omenu, "Close Background MCA",
                 "Close Background MCA", self.close_bkg_mca)

        amenu = wx.Menu()
        MenuItem(self, amenu, "&Calibrate Energy\tCtrl+E",
                 "Calibrate Energy",  self.onCalibrateEnergy)
        MenuItem(self, amenu, "Fit Peaks\tCtrl+F",
                 "Fit Peaks in spectra",  self.onFitPeaks)
        self._menus = [(fmenu, '&File'),
                       (omenu, '&Options'),
                       (amenu, '&Analysis')]

    def createMenus(self):
        self.menubar = wx.MenuBar()
        self.createBaseMenus()
        self.createCustomMenus()
        for menu, title in self._menus:
            self.menubar.Append(menu, title)
        self.SetMenuBar(self.menubar)
        self.Bind(wx.EVT_CLOSE, self.onExit)

    def onSavePNG(self, event=None):
        if self.panel is not None:
            self.panel.save_figure(event=event)

    def onCopyImage(self, event=None):
        if self.panel is not None:
            self.panel.canvas.Copy_to_Clipboard(event=event)

    def onPageSetup(self, event=None):
        if self.panel is not None:
            self.panel.PrintSetup(event=event)

    def onPrintPreview(self, event=None):
        if self.panel is not None:
            self.panel.PrintPreview(event=event)

    def onPrint(self, event=None):
        if self.panel is not None:
            self.panel.Print(event=event)

    def onExit(self, event=None):
        try:
            if hasattr(self.exit_callback, '__call__'):
                self.exit_callback()
        except:
            pass
        try:
            if self.panel is not None:
                self.panel.win_config.Close(True)
            if self.panel is not None:
                self.panel.win_config.Destroy()
        except:
            pass


        try:
            self.Destroy()
        except:
            pass

    def config_colors(self, event=None):
        """show configuration frame"""
        try:
            self.win_config.Raise()
        except:
            self.win_config = ColorsFrame(parent=self)

    def config_xraylines(self, event=None):
        """show configuration frame"""
        try:
            self.win_config.Raise()
        except:
            self.win_config = XrayLinesFrame(parent=self)

    def onKLM(self, event=None):
        """selected K, L, or M Markers"""
        if self.selected_elem is not None:
            self.onShowLines(elem = self.selected_elem)

    def onToggleHold(self, event=None):
        if event.IsChecked():
            self.wids['holdbtn'].SetLabel("Hide %s" % self.selected_elem)
            self.hold_lines = self.saved_lines[:]
        else:
            self.wids['holdbtn'].SetLabel("Hold %s" % self.selected_elem)
            self.hold_lines = None
            for m in self.hold_markers:
                try:
                    m.remove()
                except:
                    pass
            self.hold_markers = []
            self.draw()

    def onSelectXrayLine(self, evt=None):
        if self.wids['xray_lines'] is None:
            return
        if not self.wids['xray_lines'].HasSelection():
            return
        item = self.wids['xray_lines'].GetSelectedRow()
        en = self.wids['xray_linesdata'][item]

        if self.highlight_xrayline is not None:
            self.highlight_xrayline.remove()

        self.energy_for_zoom = en
        self.highlight_xrayline = self.panel.axes.axvline(en,
                             color=self.conf.emph_elinecolor,
                             linewidth=2.5, zorder=-15)
        self.draw()

    def onShowLines(self, event=None, elem=None):
        if elem is None:
            elem  = event.GetString()
        try:
            vline = self.panel.axes.axvline
            elines = self.larch.symtable._xray.xray_lines(elem)
        except:
            return
        self.selected_elem = elem
        self.clear_lines()
        self.energy_for_zoom = None
        xlines = self.wids['xray_lines']
        if xlines is not None:
            xlines.DeleteAllItems()
        self.wids['xray_linesdata'] = []
        minors, majors = [], []
        conf = self.conf
        line_data = {}
        for line in (conf.K_major+conf.K_minor+conf.L_major+
                     conf.L_minor+conf.M_major):
            line_data[line] = line, -1, 0, '', ''
            if line in elines:
                dat = elines[line]
                line_data[line] = line, dat[0], dat[1], dat[2], dat[3]

        if self.wids['kseries'].IsChecked():
            majors.extend([line_data[l] for l in conf.K_major])
            minors.extend([line_data[l] for l in conf.K_minor])
        if self.wids['lseries'].IsChecked():
            majors.extend([line_data[l] for l in conf.L_major])
            minors.extend([line_data[l] for l in conf.L_minor])
        if self.wids['mseries'].IsChecked():
            majors.extend([line_data[l] for l in conf.M_major])

        self.saved_lines = majors[:] + minors[:]
        erange = [max(conf.e_min, self.xdata.min()),
                  min(conf.e_max, self.xdata.max())]

        view_mid, view_range, d1, d2 = self._getlims()
        view_emin = view_mid - view_range/2.0
        view_emax = view_mid + view_range/2.0
        for label, eev, frac, ilevel, flevel in majors:
            e = float(eev) * 0.001
            # print( 'Major ', label, eev, e, frac, ilevel, flevel)
            if (e >= erange[0] and e <= erange[1]):
                l = vline(e, color= self.conf.major_elinecolor,
                          linewidth=1.50, zorder=-5)
                l.set_label(label)
                dat = (label, "%.4f" % e, "%.4f" % frac,
                       "%s->%s" % (ilevel, flevel))
                self.wids['xray_linesdata'].append(e)
                if xlines is not None:
                    xlines.AppendItem(dat)

                self.major_markers.append(l)
                if (self.energy_for_zoom is None and
                    e > view_emin and e < view_emax):
                    self.energy_for_zoom = e

        for label, eev, frac, ilevel, flevel in minors:
            e = float(eev) * 0.001
            if (e >= erange[0] and e <= erange[1]):
                l = vline(e, color= self.conf.minor_elinecolor,
                          linewidth=1.25, zorder=-7)
                l.set_label(label)

                # dat = (label, "%.4f" % e, "%.4f" % frac,
                #       "%s->%s" % (ilevel, flevel))
                dat = (label,  e, "%.4f" % frac,
                       "%s->%s" % (ilevel, flevel))

                self.wids['xray_linesdata'].append(e)
                if xlines is not None:
                    xlines.AppendItem(dat)
                self.minor_markers.append(l)

        if not self.wids['holdbtn'].GetValue():
            self.wids['holdbtn'].SetLabel("Hold %s" % elem)
        elif self.hold_lines is not None:
            for label, eev, frac, ilevel, flevel in self.hold_lines:
                e = float(eev) * 0.001
                if (e >= erange[0] and e <= erange[1]):
                    l = vline(e, color = self.conf.hold_elinecolor,
                              linewidth=1.5, zorder=-20, dashes=(5, 5))
                    l.set_label(label)
                    self.hold_markers.append(l)

        if xlines is not None:
            xlines.Refresh()

        for index, edges in ((0, ('K', 'M5')),
                             (1, ('L3', 'L2', 'L1'))):
            out = []
            for edge in edges:
                xex = self.larch.symtable._xray.xray_edge(elem, edge)
                if xex is None: xex = (0., 0)
                en = xex[0]*0.001
                if en > erange[0] and en < erange[1]:
                    out.append("%s=%.3f" % (edge, en))

            out = '  ' + ', '.join(out)
            self.wids['ptable'].set_subtitle(out, index=index)

        self.draw()

    def onYAxis(self, event=None):
        self.show_yaxis = self.wids['show_yaxis'].IsChecked()
        ax = self.panel.axes
        ax.yaxis.set_major_formatter(FuncFormatter(self._formaty))
        ax.get_yaxis().set_visible(self.show_yaxis)
        ax.spines['right'].set_visible(False)
        ax.yaxis.set_ticks_position('left')
        self.draw()

    def _formaty(self, val, index=0, **kws):
        try:
            decade = int(np.log10(val))
        except:
            decade = 0
        scale = 10**decade
        out = "%.1fe%i" % (val/scale, decade)
        if abs(decade) < 1.9:
            out = "%.1f" % val
        elif abs(decade) < 3.9:
            out = "%.0f" % val
        return out

    def onLogLinear(self, event=None):
        self.ylog_scale = 'log' == event.GetString()
        roiname = None
        if self.selected_roi is not None:
            roiname = self.selected_roi.name
        self.plot(self.xdata, self.ydata)
        if self.selected_elem is not None:
            self.onShowLines(elem=self.selected_elem)
        if roiname is not None:
            self.onROI(label=roiname)
        if self.y2data is not None:
            self.oplot(self.x2data, self.y2data)

    def plotmca(self, mca, title=None, set_title=True, as_mca2=False,
                fullrange=False, init=False, **kws):
        if as_mca2:
            self.mca2 = mca
            kws['new'] = False
        else:
            self.mca = mca
            self.panel.conf.show_grid = False

        if init:
            self.xview_range = (min(self.mca.energy), max(self.mca.energy))
        else:
            self.xview_range = self.panel.axes.get_axes().get_xlim()


        atitles = []
        if self.mca is not None:
            if hasattr(self.mca, 'title'):
                atitles.append(self.mca.title)
            if hasattr(self.mca, 'filename'):
                atitles.append(" File={:s}".format(self.mca.filename))
            if hasattr(self.mca, 'npixels'):
                atitles.append(" {:.0f} Pixels".format(self.mca.npixels))
            if hasattr(self.mca, 'real_time'):
                try:
                    rtime_str = " RealTime={:.2f} sec".format(self.mca.real_time)
                except ValueError:
                    rtime_str = " RealTime= %s sec".format(str(self.mca.real_time))
                atitles.append(rtime_str)

            try:
                self.plot(self.mca.energy, self.mca.counts,
                          mca=self.mca, **kws)
            except ValueError:
                pass
        if as_mca2:
            if hasattr(self.mca2, 'title'):
                atitles.append(" BG={:s}".format(self.mca2.title))
            elif  hasattr(self.mca2, 'filename'):
                atitles.append(" BG_File={:s}".format(self.mca2.filename))
            if hasattr(self.mca, 'real_time'):
                atitles.append(" BG_RealTime={:.2f} sec".format(self.mca2.real_time))

            self.oplot(self.mca2.energy, self.mca2.counts,
                       mca=self.mca2, **kws)
        if title is None:
            title =' '.join(atitles)
        if set_title:
            self.SetTitle(title)

    def plot(self, x, y=None, mca=None, init=False, **kws):
        if mca is not None:
            self.mca = mca
        mca = self.mca
        panel = self.panel
        panel.canvas.Freeze()
        panel.yformatter = self._formaty
        panel.axes.get_yaxis().set_visible(False)
        kwargs = {'xmin': 0,
                  'grid': panel.conf.show_grid,
                  'ylog_scale': self.ylog_scale,
                  'xlabel': 'E (keV)',
                  'axes_style': 'bottom',
                  'color': self.conf.spectra_color}
        kwargs.update(kws)
        self.xdata = 1.0*x[:]
        self.ydata = 1.0*y[:]
        yroi = None
        ydat = 1.0*y[:] + 1.e-9
        kwargs['ymax'] = max(ydat)*1.25
        kwargs['ymin'] = 0.9
        if self.xview_range is not None:
            kwargs['xmin'] = self.xview_range[0]
            kwargs['xmax'] = self.xview_range[1]

        if mca is not None:
            xnpts = 1.0/len(self.mca.energy)
            if not self.rois_shown:
                self.set_roilist(mca=mca)
            yroi = -1*np.ones(len(y))
            for r in mca.rois:
                if (r.left, r.right) in ((0, 0), (-1, -1)):
                    continue
                if xnpts*(r.right - r.left) > 0.5:
                    continue
                yroi[r.left:r.right] = y[r.left:r.right]
                ydat[r.left+1:r.right-1] = -1.0*y[r.left+1:r.right-1]
            yroi = np.ma.masked_less(yroi, 0)
            ydat = np.ma.masked_less(ydat, 0)

        panel.plot(x, ydat, label='spectra',  **kwargs)

        self.unzoom_all()
        if yroi is not None and yroi.max() > 0:
            kwargs['color'] = self.conf.roi_color
            panel.oplot(x, yroi, label='roi', **kwargs)

        panel.axes.get_yaxis().set_visible(self.show_yaxis)
        panel.cursor_mode = 'zoom'

        a, b, c, d = self._getlims()
        self.panel.axes.set_xlim((c, d))
        self.draw()

        panel.canvas.Thaw()
        panel.canvas.Refresh()

    def update_mca(self, counts, energy=None, with_rois=True,
                   is_mca2=False, draw=True):
        """update counts (and optionally energy) for mca, and update plot"""
        mca = self.mca
        ix = 0
        if is_mca2:
            mca = self.mca2
            ix = 2
        mca.counts = counts[:]
        if energy is not None:
            mca.energy = energy[:]
        xnpts = 1.0/len(energy)
        nrois = len(mca.rois)
        if not is_mca2 and with_rois and nrois > 0:
            yroi = -1*np.ones(len(counts))
            for r in mca.rois:
                if xnpts*(r.right - r.left) > 0.5:
                    continue
                yroi[r.left:r.right] = counts[r.left:r.right]
            yroi = np.ma.masked_less(yroi, 0)
            self.panel.update_line(1, mca.energy, yroi, draw=False,
                                   update_limits=False)

        self.panel.update_line(ix, mca.energy, counts,
                               draw=False, update_limits=False)

        max_counts = max_counts2 = max(self.mca.counts)
        try:
            max_counts2 = max(self.mca2.counts)
        except:
            pass

        self.panel.axes.set_ylim(0.9, 1.25*max(max_counts, max_counts2))
        if mca == self.mca:
            self.ydata = 1.0*counts[:]
        self.update_status()
        if draw: self.draw()

    def oplot(self, x, y, color='darkgreen', label='spectra2',
              mca=None, zorder=-2, **kws):
        if mca is not None:
            self.mca2 = mca

        self.x2data = 1.0*x[:]
        self.y2data = 1.0*y[:]
        if hasattr(self, 'ydata'):
            ymax = max(max(self.ydata), max(y))*1.25
        else:
            ymax = max(y)*1.25

        kws.update({'zorder': zorder, 'label': label,
                    'ymax' : ymax, 'axes_style': 'bottom',
                    'ylog_scale': self.ylog_scale})
        self.panel.oplot(self.x2data, self.y2data, color=color, **kws)

    def swap_mcas(self, event=None):
        if self.mca2 is None:
            return
        self.mca, self.mca2 = self.mca2, self.mca
        self.plotmca(self.mca)
        self.plotmca(self.mca2, as_mca2=True)

    def close_bkg_mca(self, event=None):
        self.mca2 = None
        self.plotmca(self.mca)


    def onReadMCAFile(self, event=None):
        dlg = wx.FileDialog(self, message="Open MCA File for reading",
                            defaultDir=os.getcwd(),
                            wildcard=FILE_WILDCARDS,
                            style = wx.FD_OPEN|wx.FD_CHANGE_DIR)

        fnew= None
        if dlg.ShowModal() == wx.ID_OK:
            fnew = os.path.abspath(dlg.GetPath())
        dlg.Destroy()

        if fnew is None:
            return
        self.mca2 = None
        if self.mca is not None:
            self.mca2 = copy.deepcopy(self.mca)

        self.mca = gsemca_group(fnew, _larch=self.larch)

        setattr(self._mcagroup, 'mca1', self.mca)
        setattr(self._mcagroup, 'mca2', self.mca2)
        self.plotmca(self.mca, show_mca2=True)

    def onReadGSEXRMFile(self, event=None, **kws):
        print( '  onReadGSEXRMFile   ')
        pass

    def onOpenEpicsMCA(self, event=None, **kws):
        print( '  onOpenEpicsMCA   ')
        pass

    def onSaveMCAFile(self, event=None, **kws):
        deffile = ''
        if hasattr(self.mca, 'sourcefile'):
            deffile = "%s%s" % (deffile, getattr(self.mca, 'sourcefile'))
        if hasattr(self.mca, 'areaname'):
            deffile = "%s%s" % (deffile, getattr(self.mca, 'areaname'))
        if deffile == '':
            deffile ='test'
        if not deffile.endswith('.mca'):
            deffile = deffile + '.mca'

        deffile = fix_filename(str(deffile))
        outfile = FileSave(self, "Save MCA File",
                           default_file=deffile,
                           wildcard=FILE_WILDCARDS)

        if outfile is not None:
            self.mca.save_mcafile(outfile)

    def onSaveColumnFile(self, event=None, **kws):
        print( '  onSaveColumnFile not yet implemented  ')
        pass

    def onCalibrateEnergy(self, event=None, **kws):
        try:
            self.win_calib.Raise()
        except:
            self.win_calib = CalibrationFrame(self, mca=self.mca,
                                              larch=self.larch)

    def onFitPeaks(self, event=None, **kws):
        try:
            self.win_fit.Raise()
        except:
            self.win_fit = FitSpectraFrame(self)

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

    def onAbout(self, event=None):
        dlg = wx.MessageDialog(self,
                               """XRF Spectral Viewer
                               Matt Newville <newville @ cars.uchicago.edu>
                               """,
                               "About XRF Viewer",
                               wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onClose(self,event):
        self.Destroy()

    def onReadFile(self, event=None):
        dlg = wx.FileDialog(self, message="Read MCA File",
                            defaultDir=os.getcwd(),
                            wildcard=FILE_WILDCARDS,
                            style=wx.FD_OPEN)
        path, re1ad = None, False
        if dlg.ShowModal() == wx.ID_OK:
            read = True
            path = dlg.GetPath().replace('\\', '/')
            if path in self.filemap:
                read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path,
                                           'Re-read file?', style=wx.YES_NO))
        dlg.Destroy()

        if read:
            try:
                parent, fname = os.path.split(path)
            except:
                return
Ejemplo n.º 3
0
 def config_colors(self, event=None):
     """show configuration frame"""
     try:
         self.win_config.Raise()
     except:
         self.win_config = ColorsFrame(parent=self)