source: sasview/src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter2D.py @ 1a696bf

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 1a696bf was 1a696bf, checked in by lewis, 8 years ago

Don't reuse WX IDs in Plotter2D sub-menu (fixes #617)

  • Property mode set to 100644
File size: 29.4 KB
RevLine 
[1bf33c1]1
[d955bf19]2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
[c039589]5#project funded by the US National Science Foundation.
[d955bf19]6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
[1bf33c1]11
12
13import wx
[32c0841]14import sys
15import math
[003fa4e]16import numpy
[c039589]17import logging
[d7bb526]18from sas.sasgui.plottools.PlotPanel import PlotPanel
19from sas.sasgui.plottools.plottables import Graph
20from sas.sasgui.plottools.TextDialog import TextDialog
[d85c194]21from sas.sasgui.guiframe.events import StatusEvent
22from sas.sasgui.guiframe.events import NewPlotEvent
23from sas.sasgui.guiframe.events import PanelOnFocusEvent
24from sas.sasgui.guiframe.events import SlicerEvent
25from sas.sasgui.guiframe.utils import PanelMenu
26from  sas.sasgui.guiframe.local_perspectives.plotting.binder import BindArtist
[1bf33c1]27from Plotter1D import ModelPanel1D
[d7bb526]28from sas.sasgui.plottools.toolbar import NavigationToolBar
[49a470d]29from matplotlib.font_manager import FontProperties
[8a687cfd]30from graphAppearance import graphAppearance
[32c0841]31(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
[0d9dae8]32
33DEFAULT_QMAX = 0.05
[1bf33c1]34DEFAULT_QSTEP = 0.001
35DEFAULT_BEAM = 0.005
[ef0c170]36BIN_WIDTH = 1.0
[32c0841]37
[d955bf19]38
[8a687cfd]39def find_key(dic, val):
40    """return the key of dictionary dic given the value"""
41    return [k for k, v in dic.iteritems() if v == val][0]
42
43
[ac8671e]44class NavigationToolBar2D(NavigationToolBar):
[d955bf19]45    """
46    """
[ac8671e]47    def __init__(self, canvas, parent=None):
48        NavigationToolBar.__init__(self, canvas=canvas, parent=parent)
[c039589]49
[ac8671e]50    def delete_option(self):
51        """
[d955bf19]52        remove default toolbar item
[ac8671e]53        """
54        #delete reset button
[c039589]55        self.DeleteToolByPos(0)
[ac8671e]56        #delete dragging
[c039589]57        self.DeleteToolByPos(2)
[ac8671e]58        #delete unwanted button that configures subplot parameters
59        self.DeleteToolByPos(4)
[c039589]60
[ac8671e]61    def add_option(self):
62        """
[d955bf19]63        add item to the toolbar
[ac8671e]64        """
[dc51a7f]65        #add button
66        id_context = wx.NewId()
67        context_tip = 'Graph Menu'
[c039589]68        context = wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR)
69        self.InsertSimpleTool(0, id_context, context, context_tip, context_tip)
70        wx.EVT_TOOL(self, id_context, self.parent.onToolContextMenu)
[dc51a7f]71        self.InsertSeparator(1)
[ac8671e]72        #add print button
73        id_print = wx.NewId()
[c039589]74        print_bmp = wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR)
75        self.AddSimpleTool(id_print, print_bmp, 'Print', 'Activate printing')
[ac8671e]76        wx.EVT_TOOL(self, id_print, self.on_print)
[c039589]77
78
[ac8671e]79class ModelPanel2D(ModelPanel1D):
[1bf33c1]80    """
[d955bf19]81    Plot panel for use with the GUI manager
[1bf33c1]82    """
[c039589]83
[1bf33c1]84    ## Internal name for the AUI manager
85    window_name = "plotpanel"
86    ## Title to appear on top of the window
87    window_caption = "Plot Panel"
88    ## Flag to tell the GUI manager that this panel is not
89    #  tied to any perspective
90    ALWAYS_ON = True
91    ## Group ID
92    group_id = None
[c039589]93
94
95    def __init__(self, parent, id=-1, data2d=None, color=None,
[32c0841]96                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
[1bf33c1]97        """
[d955bf19]98        Initialize the panel
[1bf33c1]99        """
[32c0841]100        ModelPanel1D.__init__(self, parent, id=id, style=style, **kwargs)
[1bf33c1]101        self.parent = parent
[ae84427]102        ## Reference to the parent window
103        if hasattr(parent, "parent"):
104            self.parent = self.parent.parent
[6c0568b]105        ## Dictionary containing Plottables
[1bf33c1]106        self.plots = {}
[c039589]107        ## Save reference of the current plotted
[6c0568b]108        self.data2D = data2d
[1bf33c1]109        ## Unique ID (from gui_manager)
110        self.uid = None
111        ## Action IDs for internal call-backs
112        self.action_ids = {}
[6c0568b]113        ## Create Artist and bind it
[1bf33c1]114        self.connect = BindArtist(self.subplot.figure)
[6c0568b]115        ## Beam stop
[1bf33c1]116        self.beamstop_radius = DEFAULT_BEAM
[6c0568b]117        ## to set the order of lines drawn first.
[f15ed33]118        self.slicer_z = 5
[6c0568b]119        ## Reference to the current slicer
[1bf33c1]120        self.slicer = None
[c039589]121        ## event to send slicer info
[d468daa]122        self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
[c039589]123
[6c0568b]124        self.axes_frozen = False
125        ## panel that contains result from slicer motion (ex: Boxsum info)
[32c0841]126        self.panel_slicer = None
[49a470d]127        self.title_label = None
128        self.title_font = None
129        self.title_color = 'black'
[c039589]130        ## Graph
[1bf33c1]131        self.graph = Graph()
132        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
[32c0841]133        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
[1bf33c1]134        self.graph.render(self)
[c039589]135        ## store default value of zmin and zmax
[8dfdd20]136        self.default_zmin_ctl = self.zmin_2D
137        self.default_zmax_ctl = self.zmax_2D
[3e001f9]138
139    def on_plot_qrange(self, event=None):
140        """
141        On Qmin Qmax vertical line event
142        """
143        # Not implemented
144        if event == None:
145            return
[c039589]146        event.Skip()
147
148    def onLeftDown(self, event):
[a07e72f]149        """
150        left button down and ready to drag
[c039589]151
[a07e72f]152        """
153        # Check that the LEFT button was pressed
[c039589]154        PlotPanel.onLeftDown(self, event)
[e85e2bc]155        ax = event.inaxes
156        if ax != None:
157            # data coordinate position
[c039589]158            pos_x = "%8.3g" % event.xdata
159            pos_y = "%8.3g" % event.ydata
[e85e2bc]160            position = "x: %s    y: %s" % (pos_x, pos_y)
161            wx.PostEvent(self.parent, StatusEvent(status=position))
[a07e72f]162        self.plottable_selected(self.data2D.id)
163        self._manager.set_panel_on_focus(self)
164        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
[c039589]165
[ac8671e]166    def add_toolbar(self):
167        """
[d955bf19]168        add toolbar
[ac8671e]169        """
170        self.enable_toolbar = True
[32c0841]171        self.toolbar = NavigationToolBar2D(parent=self, canvas=self.canvas)
[ac8671e]172        self.toolbar.Realize()
173        # On Windows platform, default window size is incorrect, so set
174        # toolbar width to figure width.
[c039589]175        _, th = self.toolbar.GetSizeTuple()
176        fw, _ = self.canvas.GetSizeTuple()
[ac8671e]177        self.toolbar.SetSize(wx.Size(fw, th))
[c039589]178        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
[ac8671e]179        # update the axes menu on the toolbar
180        self.toolbar.update()
[c039589]181
[a07e72f]182    def plot_data(self, data):
[1bf33c1]183        """
[d955bf19]184        Data is ready to be displayed
[c039589]185
[d955bf19]186        :TODO: this name should be changed to something more appropriate
187             Don't forget that changing this name will mean changing code
188             in plotting.py
[c039589]189
[d955bf19]190        :param event: data event
[1bf33c1]191        """
[e4a703a]192        xlo = None
193        xhi = None
[c039589]194        ylo = None
[e4a703a]195        yhi = None
[b837e1f]196        if data.__class__.__name__ == 'Data1D':
197            return
[6c0568b]198        ## Update self.data2d with the current plot
[857d00f]199        self.data2D = data
[a07e72f]200        if data.id in self.plots.keys():
201            #replace
[e4a703a]202            xlo, xhi = self.subplot.get_xlim()
203            ylo, yhi = self.subplot.get_ylim()
[a07e72f]204            self.graph.replace(data)
205            self.plots[data.id] = data
[ab8f936]206        else:
[a07e72f]207            self.plots[data.id] = data
[c039589]208            self.graph.add(self.plots[data.id])
[ac9a5f6]209            # update qmax with the new xmax of data plotted
[c039589]210            self.qmax = data.xmax
[32c0841]211        self.slicer = None
[1bf33c1]212        # Check axis labels
213        #TODO: Should re-factor this
[c039589]214        ## render the graph with its new content
[7fff5cd]215        #data2D: put 'Pixel (Number)' for axis title and unit in case of having no detector info and none in _units
[c039589]216        if len(data.detector) < 1:
[a07e72f]217            if len(data._xunit) < 1 and len(data._yunit) < 1:
218                data._xaxis = '\\rm{x}'
219                data._yaxis = '\\rm{y}'
220                data._xunit = 'pixel'
221                data._yunit = 'pixel'
[857d00f]222        # graph properties
[a07e72f]223        self.graph.xaxis(data._xaxis, data._xunit)
224        self.graph.yaxis(data._yaxis, data._yunit)
[c039589]225        if self._is_changed_legend_label:
226            data.label = self.title_label
[fe48fcc]227        if data.label == None:
[c039589]228            data.label = data.name
[49a470d]229        if not self.title_font:
230            self.graph.title(data.label)
231            self.graph.render(self)
[857d00f]232            # Set the axis labels on subplot
233            self._set_axis_labels()
[49a470d]234            self.draw_plot()
235        else:
236            self.graph.render(self)
237            self.draw_plot()
238            self.subplot.set_title(label=data.label,
239                                   fontproperties=self.title_font,
240                                   color=self.title_color)
[c039589]241            self.subplot.figure.canvas.draw_idle()
242        ## store default value of zmin and zmax
[8dfdd20]243        self.default_zmin_ctl = self.zmin_2D
244        self.default_zmax_ctl = self.zmax_2D
[cafa75f]245        if not self.is_zoomed:
[7022fdc]246            return
[e4a703a]247        # Recover the x,y limits
[c039589]248        if xlo and xhi and ylo and yhi:
249            if xlo > data.xmin and xhi < data.xmax and \
250                        ylo > data.ymin and yhi < data.ymax:
251                self.subplot.set_xlim((xlo, xhi))
252                self.subplot.set_ylim((ylo, yhi))
253            else:
[fe48fcc]254                self.toolbar.update()
[cafa75f]255                self.is_zoomed = False
[a4964b8e]256
[857d00f]257    def _set_axis_labels(self):
258        """
259        Set axis labels
260        """
261        data = self.data2D
262        # control axis labels from the panel itself
263        yname, yunits = data.get_yaxis()
264        if self.yaxis_label != None:
265            yname = self.yaxis_label
266            yunits = self.yaxis_unit
267        else:
268            self.yaxis_label = yname
269            self.yaxis_unit = yunits
270        xname, xunits = data.get_xaxis()
271        if self.xaxis_label != None:
272            xname = self.xaxis_label
273            xunits = self.xaxis_unit
274        else:
275            self.xaxis_label = xname
276            self.xaxis_unit = xunits
[c039589]277        self.xaxis(xname, xunits, self.xaxis_font,
[2a09236]278                   self.xaxis_color, self.xaxis_tick)
[c039589]279        self.yaxis(yname, yunits, self.yaxis_font,
[2a09236]280                   self.yaxis_color, self.yaxis_tick)
[c039589]281
[1bf33c1]282    def onContextMenu(self, event):
283        """
[d955bf19]284        2D plot context menu
[c039589]285
[d955bf19]286        :param event: wx context event
[c039589]287
[d955bf19]288        """
[6f16e25]289        ids = iter(self._menu_ids)
[1bf33c1]290        slicerpop = PanelMenu()
291        slicerpop.set_plots(self.plots)
292        slicerpop.set_graph(self.graph)
[c039589]293
[6f16e25]294        wx_id = ids.next()
[1a696bf]295        slicerpop.Append(wx_id, '&Save Image', 'Save image as png')
[c039589]296        wx.EVT_MENU(self, wx_id, self.onSaveImage)
297
[6f16e25]298        wx_id = ids.next()
[c039589]299        slicerpop.Append(wx_id, '&Print Image', 'Print image')
300        wx.EVT_MENU(self, wx_id, self.onPrint)
301
[6f16e25]302        wx_id = ids.next()
[c039589]303        slicerpop.Append(wx_id, '&Copy to Clipboard', 'Copy to the clipboard')
304        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
[c5a769e]305        slicerpop.AppendSeparator()
[ed8ad21]306        # saving data
307        plot = self.data2D
[6f16e25]308        wx_id = ids.next()
[c039589]309        slicerpop.Append(wx_id, "&Data Info")
310        wx.EVT_MENU(self, wx_id, self._onDataShow)
[0aca693]311
[6f16e25]312        wx_id = ids.next()
[c039589]313        slicerpop.Append(wx_id, "&Save as a File (DAT)")
314        self.action_ids[str(wx_id)] = plot
315        wx.EVT_MENU(self, wx_id, self._onSave)
[ed8ad21]316
[9a585d0]317        slicerpop.AppendSeparator()
[c039589]318        if len(self.data2D.detector) == 1:
[940aca7]319            item_list = self.parent.get_current_context_menu(self)
[6d727ae]320            if (not item_list == None) and (not len(item_list) == 0) and\
[c039589]321                self.data2D.name.split(" ")[0] != 'Residuals':
[1a696bf]322                for item, wx_id in zip(item_list, [ids.next() for i in range(len(item_list))]):
[32c0841]323                    try:
[c039589]324                        slicerpop.Append(wx_id, item[0], item[1])
325                        wx.EVT_MENU(self, wx_id, item[2])
[32c0841]326                    except:
327                        msg = "ModelPanel1D.onContextMenu: "
[c039589]328                        msg += "bad menu item  %s" % sys.exc_value
[32c0841]329                        wx.PostEvent(self.parent, StatusEvent(status=msg))
330                slicerpop.AppendSeparator()
[c039589]331
[6f16e25]332            wx_id = ids.next()
[c039589]333            slicerpop.Append(wx_id, '&Perform Circular Average')
334            wx.EVT_MENU(self, wx_id, self.onCircular) \
[6d727ae]335            # For Masked Data
336            if not plot.mask.all():
[6f16e25]337                wx_id = ids.next()
[c039589]338                slicerpop.Append(wx_id, '&Masked Circular Average')
339                wx.EVT_MENU(self, wx_id, self.onMaskedCircular)
[6f16e25]340            wx_id = ids.next()
[c039589]341            slicerpop.Append(wx_id, '&Sector [Q View]')
342            wx.EVT_MENU(self, wx_id, self.onSectorQ)
[6f16e25]343            wx_id = ids.next()
[c039589]344            slicerpop.Append(wx_id, '&Annulus [Phi View ]')
345            wx.EVT_MENU(self, wx_id, self.onSectorPhi)
[6f16e25]346            wx_id = ids.next()
[c039589]347            slicerpop.Append(wx_id, '&Box Sum')
348            wx.EVT_MENU(self, wx_id, self.onBoxSum)
[6f16e25]349            wx_id = ids.next()
[c039589]350            slicerpop.Append(wx_id, '&Box Averaging in Qx')
351            wx.EVT_MENU(self, wx_id, self.onBoxavgX)
[6f16e25]352            wx_id = ids.next()
[c039589]353            slicerpop.Append(wx_id, '&Box Averaging in Qy')
354            wx.EVT_MENU(self, wx_id, self.onBoxavgY)
[32c0841]355            if self.slicer != None:
[6f16e25]356                wx_id = ids.next()
[c039589]357                slicerpop.Append(wx_id, '&Clear Slicer')
358                wx.EVT_MENU(self, wx_id, self.onClearSlicer)
359                if self.slicer.__class__.__name__ != "BoxSum":
[6f16e25]360                    wx_id = ids.next()
[c039589]361                    slicerpop.Append(wx_id, '&Edit Slicer Parameters')
362                    wx.EVT_MENU(self, wx_id, self._onEditSlicer)
363            slicerpop.AppendSeparator()
364
[6f16e25]365        wx_id = ids.next()
[c039589]366        slicerpop.Append(wx_id, '&Edit Graph Label', 'Edit Graph Label')
367        wx.EVT_MENU(self, wx_id, self.onEditLabels)
[fe48fcc]368        slicerpop.AppendSeparator()
[c039589]369
[8a687cfd]370        # ILL mod here
371
[6f16e25]372        wx_id = ids.next()
[c039589]373        slicerpop.Append(wx_id, '&Modify graph appearance', 'Modify graph appearance')
374        wx.EVT_MENU(self, wx_id, self.modifyGraphAppearance)
[857d00f]375        slicerpop.AppendSeparator()
[8a687cfd]376
[6f16e25]377        wx_id = ids.next()
[c039589]378        slicerpop.Append(wx_id, '&2D Color Map')
379        wx.EVT_MENU(self, wx_id, self._onEditDetector)
[857d00f]380        slicerpop.AppendSeparator()
[c039589]381
[6f16e25]382        wx_id = ids.next()
[c039589]383        slicerpop.Append(wx_id, '&Toggle Linear/Log Scale')
384        wx.EVT_MENU(self, wx_id, self._onToggleScale)
385
[53cf669]386        slicerpop.AppendSeparator()
[6f16e25]387        wx_id = ids.next()
[c039589]388        slicerpop.Append(wx_id, '&Window Title')
389        wx.EVT_MENU(self, wx_id, self.onChangeCaption)
390
[dc51a7f]391        try:
392            pos_evt = event.GetPosition()
393            pos = self.ScreenToClient(pos_evt)
394        except:
395            pos_x, pos_y = self.toolbar.GetPositionTuple()
396            pos = (pos_x, pos_y + 5)
[1bf33c1]397        self.PopupMenu(slicerpop, pos)
[c039589]398
[fe48fcc]399    def onEditLabels(self, event):
400        """
401        Edit legend label
402        """
[657e52c]403        try:
404            selected_plot = self.plots[self.graph.selected_plottable]
405        except:
406            selected_plot = self.plots[self.data2D.id]
[fe48fcc]407        label = selected_plot.label
[49a470d]408        dial = TextDialog(None, -1, 'Change Label', label)
[fe48fcc]409        if dial.ShowModal() == wx.ID_OK:
[49a470d]410            try:
411                FONT = FontProperties()
412                newlabel = dial.getText()
413                font = FONT.copy()
414                font.set_size(dial.getSize())
415                font.set_family(dial.getFamily())
416                font.set_style(dial.getStyle())
417                font.set_weight(dial.getWeight())
418                colour = dial.getColor()
419                if len(newlabel) > 0:
[53cf669]420                    # update Label
[49a470d]421                    selected_plot.label = newlabel
[53cf669]422                    self.graph.title(newlabel)
[49a470d]423                    self.title_label = selected_plot.label
424                    self.title_font = font
425                    self.title_color = colour
426                    ## render the graph
427                    self.subplot.set_title(label=self.title_label,
428                                           fontproperties=self.title_font,
429                                           color=self.title_color)
[36288ca]430                    self._is_changed_legend_label = True
[c039589]431                    self.subplot.figure.canvas.draw_idle()
[49a470d]432            except:
[c039589]433                msg = "Add Text: Error. Check your property values..."
434                logging.error(msg)
[49a470d]435                if self.parent != None:
[c039589]436                    wx.PostEvent(self.parent, StatusEvent(status=msg))
[fe48fcc]437        dial.Destroy()
[c039589]438
[ea290ee]439    def _onEditDetector(self, event):
[6d920cd]440        """
[d955bf19]441        Allow to view and edits  detector parameters
[c039589]442
[d955bf19]443        :param event: wx.menu event
[c039589]444
[6d920cd]445        """
[ea290ee]446        import detector_dialog
[c039589]447        dialog = detector_dialog.DetectorDialog(self, -1, base=self.parent,
448                                                reset_zmin_ctl=self.default_zmin_ctl,
449                                                reset_zmax_ctl=self.default_zmax_ctl, cmap=self.cmap)
[6c0568b]450        ## info of current detector and data2D
[ea290ee]451        xnpts = len(self.data2D.x_bins)
452        ynpts = len(self.data2D.y_bins)
453        xmax = max(self.data2D.xmin, self.data2D.xmax)
454        ymax = max(self.data2D.ymin, self.data2D.ymax)
[32c0841]455        qmax = math.sqrt(math.pow(xmax, 2) + math.pow(ymax, 2))
[6c0568b]456        ## set dialog window content
[c039589]457        dialog.setContent(xnpts=xnpts, ynpts=ynpts, qmax=qmax,
458                          beam=self.data2D.xmin,
459                          zmin=self.zmin_2D,
460                          zmax=self.zmax_2D)
[ea290ee]461        if dialog.ShowModal() == wx.ID_OK:
462            evt = dialog.getContent()
463            self.zmin_2D = evt.zmin
464            self.zmax_2D = evt.zmax
[32c0841]465            self.cmap = evt.cmap
[ea290ee]466        dialog.Destroy()
[6c0568b]467        ## Redraw the current image
[32c0841]468        self.image(data=self.data2D.data,
[20b6760]469                   qx_data=self.data2D.qx_data,
470                   qy_data=self.data2D.qy_data,
[c039589]471                   xmin=self.data2D.xmin,
472                   xmax=self.data2D.xmax,
473                   ymin=self.data2D.ymin,
474                   ymax=self.data2D.ymax,
475                   zmin=self.zmin_2D,
476                   zmax=self.zmax_2D,
477                   cmap=self.cmap,
[32c0841]478                   color=0, symbol=0, label=self.data2D.name)
[ea290ee]479        self.subplot.figure.canvas.draw_idle()
[c039589]480
[1bf33c1]481    def freeze_axes(self):
[d955bf19]482        """
483        """
[1bf33c1]484        self.axes_frozen = True
[c039589]485
[1bf33c1]486    def thaw_axes(self):
[d955bf19]487        """
488        """
[1bf33c1]489        self.axes_frozen = False
[c039589]490
491    def onMouseMotion(self, event):
[d955bf19]492        """
493        """
[1bf33c1]494        pass
[c039589]495
[1bf33c1]496    def onWheel(self, event):
[d955bf19]497        """
498        """
[c039589]499        pass
500
[1bf33c1]501    def update(self, draw=True):
502        """
[c039589]503        Respond to changes in the model by recalculating the
[d955bf19]504        profiles and resetting the widgets.
[1bf33c1]505        """
[6d727ae]506        self.draw_plot()
[c039589]507
[1bf33c1]508    def _getEmptySlicerEvent(self):
[6c0568b]509        """
[c039589]510        create an empty slicervent
[6c0568b]511        """
[32c0841]512        return SlicerEvent(type=None, params=None, obj_class=None)
[c039589]513
[1bf33c1]514    def _onEVT_INTERNAL(self, event):
515        """
[d955bf19]516        Draw the slicer
[c039589]517
[d955bf19]518        :param event: wx.lib.newevent (SlicerEvent) containing slicer
[6c0568b]519            parameter
[c039589]520
[1bf33c1]521        """
522        self._setSlicer(event.slicer)
[c039589]523
[1bf33c1]524    def _setSlicer(self, slicer):
[6c0568b]525        """
[d955bf19]526        Clear the previous slicer and create a new one.Post an internal
527        event.
[c039589]528
[d955bf19]529        :param slicer: slicer class to create
[c039589]530
[6c0568b]531        """
532        ## Clear current slicer
[c039589]533        if not self.slicer == None:
534            self.slicer.clear()
535        ## Create a new slicer
[1bf33c1]536        self.slicer_z += 1
537        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
[240c805]538        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
539        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
[6c0568b]540        ## Draw slicer
[1bf33c1]541        self.update()
542        self.slicer.update()
[c039589]543        msg = "Plotter2D._setSlicer  %s" % self.slicer.__class__.__name__
[32c0841]544        wx.PostEvent(self.parent, StatusEvent(status=msg))
[1bf33c1]545        # Post slicer event
546        event = self._getEmptySlicerEvent()
547        event.type = self.slicer.__class__.__name__
548        event.obj_class = self.slicer.__class__
549        event.params = self.slicer.get_params()
[d468daa]550        wx.PostEvent(self, event)
[c039589]551
[6d727ae]552    def onMaskedCircular(self, event):
553        """
554        perform circular averaging on Data2D with mask if it exists
[c039589]555
[6d727ae]556        :param event: wx.menu event
[c039589]557
[6d727ae]558        """
559        self.onCircular(event, True)
[c039589]560
[6d727ae]561    def onCircular(self, event, ismask=False):
[1bf33c1]562        """
[d955bf19]563        perform circular averaging on Data2D
[c039589]564
[d955bf19]565        :param event: wx.menu event
[c039589]566
[1bf33c1]567        """
[003fa4e]568        # Find the best number of bins
569        npt = math.sqrt(len(self.data2D.data[numpy.isfinite(self.data2D.data)]))
570        npt = math.floor(npt)
[b699768]571        from sas.sascalc.dataloader.manipulations import CircularAverage
[6c0568b]572        ## compute the maximum radius of data2D
[c039589]573        self.qmax = max(math.fabs(self.data2D.xmax),
[32c0841]574                        math.fabs(self.data2D.xmin))
575        self.ymax = max(math.fabs(self.data2D.ymax),
576                        math.fabs(self.data2D.ymin))
[c039589]577        self.radius = math.sqrt(math.pow(self.qmax, 2) + math.pow(self.ymax, 2))
[6c0568b]578        ##Compute beam width
[c039589]579        bin_width = (self.qmax + self.qmax) / npt
[6c0568b]580        ## Create data1D circular average of data2D
[c039589]581        Circle = CircularAverage(r_min=0, r_max=self.radius,
[32c0841]582                                 bin_width=bin_width)
[6d727ae]583        circ = Circle(self.data2D, ismask=ismask)
[d85c194]584        from sas.sasgui.guiframe.dataFitting import Data1D
[32c0841]585        if hasattr(circ, "dxl"):
586            dxl = circ.dxl
[1bf33c1]587        else:
[32c0841]588            dxl = None
589        if hasattr(circ, "dxw"):
590            dxw = circ.dxw
[1bf33c1]591        else:
[32c0841]592            dxw = None
[003fa4e]593
594        new_plot = Data1D(x=circ.x, y=circ.y, dy=circ.dy, dx=circ.dx)
[32c0841]595        new_plot.dxl = dxl
596        new_plot.dxw = dxw
597        new_plot.name = "Circ avg " + self.data2D.name
598        new_plot.source = self.data2D.source
599        #new_plot.info = self.data2D.info
[1bf33c1]600        new_plot.interactive = True
[32c0841]601        new_plot.detector = self.data2D.detector
[c039589]602
[6c0568b]603        ## If the data file does not tell us what the axes are, just assume...
[32c0841]604        new_plot.xaxis("\\rm{Q}", "A^{-1}")
[2ca51f44]605        if hasattr(self.data2D, "scale") and \
606                    self.data2D.scale == 'linear':
607            new_plot.ytransform = 'y'
608            new_plot.yaxis("\\rm{Residuals} ", "normalized")
[6d727ae]609        else:
610            new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
611
[c039589]612        new_plot.group_id = "2daverage" + self.data2D.name
[32c0841]613        new_plot.id = "Circ avg " + self.data2D.name
614        new_plot.is_data = True
[657e52c]615        self.parent.update_theory(data_id=self.data2D.id, \
[13382fc7]616                                       theory=new_plot)
[c039589]617        wx.PostEvent(self.parent,
[32c0841]618                     NewPlotEvent(plot=new_plot, title=new_plot.name))
[c039589]619
[1bf33c1]620    def _onEditSlicer(self, event):
[6c0568b]621        """
[c039589]622        Is available only when a slicer is drawn.Create a dialog
[d955bf19]623        window where the user can enter value to reset slicer
624        parameters.
[c039589]625
[d955bf19]626        :param event: wx.menu event
[c039589]627
[6c0568b]628        """
[32c0841]629        if self.slicer != None:
[1bf33c1]630            from SlicerParameters import SlicerParameterPanel
[4f8a00c]631            dialog = SlicerParameterPanel(self, -1, "Slicer Parameters")
[1bf33c1]632            dialog.set_slicer(self.slicer.__class__.__name__,
[c039589]633                              self.slicer.get_params())
[1bf33c1]634            if dialog.ShowModal() == wx.ID_OK:
[c039589]635                dialog.Destroy()
636
[1bf33c1]637    def onSectorQ(self, event):
638        """
[d955bf19]639        Perform sector averaging on Q and draw sector slicer
[1bf33c1]640        """
[ef0c170]641        from SectorSlicer import SectorInteractor
[1bf33c1]642        self.onClearSlicer(event)
[32c0841]643        wx.PostEvent(self, InternalEvent(slicer=SectorInteractor))
[c039589]644
[1bf33c1]645    def onSectorPhi(self, event):
646        """
[d955bf19]647        Perform sector averaging on Phi and draw annulus slicer
[1bf33c1]648        """
[ef0c170]649        from AnnulusSlicer import AnnulusInteractor
[1bf33c1]650        self.onClearSlicer(event)
[32c0841]651        wx.PostEvent(self, InternalEvent(slicer=AnnulusInteractor))
[c039589]652
[d955bf19]653    def onBoxSum(self, event):
654        """
655        """
[d85c194]656        from sas.sasgui.guiframe.gui_manager import MDIFrame
[7ab9241]657        from boxSum import BoxSum
658        self.onClearSlicer(event)
[54cc36a]659        self.slicer_z += 1
[c039589]660        self.slicer = BoxSum(self, self.subplot, zorder=self.slicer_z)
[54cc36a]661        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
662        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
663        self.update()
664        self.slicer.update()
[6c0568b]665        ## Value used to initially set the slicer panel
666        params = self.slicer.get_params()
667        ## Create a new panel to display results of summation of Data2D
[8a7a21b]668        from slicerpanel import SlicerPanel
[ae84427]669        win = MDIFrame(self.parent, None, 'None', (100, 200))
670        new_panel = SlicerPanel(parent=win, id=-1,
[c039589]671                                base=self, type=self.slicer.__class__.__name__,
672                                params=params, style=wx.RAISED_BORDER)
673
[32c0841]674        new_panel.window_caption = self.slicer.__class__.__name__ + " " + \
675                                    str(self.data2D.name)
[c039589]676        new_panel.window_name = self.slicer.__class__.__name__ + " " + \
[32c0841]677                                    str(self.data2D.name)
[6c0568b]678        ## Store a reference of the new created panel
[c039589]679
[6c0568b]680        ## save the window_caption of the new panel in the current slicer
[32c0841]681        self.slicer.set_panel_name(name=new_panel.window_caption)
[c039589]682        ## post slicer panel to guiframe to display it
[d85c194]683        from sas.sasgui.guiframe.events import SlicerPanelEvent
[c039589]684
[ae84427]685        win.set_panel(new_panel)
686        new_panel.frame = win
687        wx.PostEvent(self.parent, SlicerPanelEvent(panel=new_panel,
[c039589]688                                                   main_panel=self))
[ae84427]689        wx.CallAfter(new_panel.frame.Show)
690        self.panel_slicer = new_panel
[c039589]691
692    def onBoxavgX(self, event):
[6c0568b]693        """
[d955bf19]694        Perform 2D data averaging on Qx
695        Create a new slicer .
[c039589]696
[d955bf19]697        :param event: wx.menu event
[6c0568b]698        """
[8a7a21b]699        from boxSlicer import BoxInteractorX
[38224f10]700        self.onClearSlicer(event)
[32c0841]701        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorX))
[c039589]702
703    def onBoxavgY(self, event):
[6c0568b]704        """
[d955bf19]705        Perform 2D data averaging on Qy
706        Create a new slicer .
[c039589]707
[d955bf19]708        :param event: wx.menu event
[c039589]709
[6c0568b]710        """
[8a7a21b]711        from boxSlicer import BoxInteractorY
712        self.onClearSlicer(event)
[32c0841]713        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorY))
[c039589]714
[1bf33c1]715    def onClearSlicer(self, event):
716        """
[d955bf19]717        Clear the slicer on the plot
[1bf33c1]718        """
[32c0841]719        if not self.slicer == None:
[1bf33c1]720            self.slicer.clear()
721            self.subplot.figure.canvas.draw()
722            self.slicer = None
723            # Post slicer None event
724            event = self._getEmptySlicerEvent()
[d468daa]725            wx.PostEvent(self, event)
[c039589]726
[ed8ad21]727    def _onSave(self, evt):
728        """
729        Save a data set to a dat(text) file
[c039589]730
[ed8ad21]731        :param evt: Menu event
[c039589]732
[ed8ad21]733        """
[c039589]734        event_id = str(evt.GetId())
[c553b18]735        if self.parent != None:
736            self._default_save_location = self.parent._default_save_location
[3858e0f]737        default_name = self.plots[self.graph.selected_plottable].label
738        if default_name.count('.') > 0:
739            default_name = default_name.split('.')[0]
740        default_name += "_out"
[c039589]741        if event_id in self.action_ids:
[176fbf1]742            self.parent.save_data2d(self.data2D, default_name)
[c039589]743
[0aca693]744    def _onDataShow(self, evt):
745        """
746        Show the data set in text
[c039589]747
[0aca693]748        :param evt: Menu event
[c039589]749
[0aca693]750        """
751        menu = evt.GetEventObject()
[c039589]752        event_id = evt.GetId()
753        self.set_selected_from_menu(menu, event_id)
[0aca693]754        data = self.plots[self.graph.selected_plottable]
755        default_name = data.label
756        if default_name.count('.') > 0:
757            default_name = default_name.split('.')[0]
758        #default_name += "_out"
759        if self.parent != None:
760            self.parent.show_data2d(data, default_name)
[8a687cfd]761
[c039589]762    def modifyGraphAppearance(self, e):
763        self.graphApp = graphAppearance(self, 'Modify graph appearance', legend=False)
764        self.graphApp.setDefaults(self.grid_on, self.legend_on,
765                                  self.xaxis_label, self.yaxis_label,
766                                  self.xaxis_unit, self.yaxis_unit,
767                                  self.xaxis_font, self.yaxis_font,
768                                  find_key(self.get_loc_label(), self.legendLoc),
769                                  self.xcolor, self.ycolor,
770                                  self.is_xtick, self.is_ytick)
[8a687cfd]771        self.graphApp.Bind(wx.EVT_CLOSE, self.on_graphApp_close)
772
[c039589]773    def on_graphApp_close(self, e):
774        """
775            Gets values from graph appearance dialog and sends them off
776            to modify the plot
777        """
[8a687cfd]778        self.onGridOnOff(self.graphApp.get_togglegrid())
779        self.xaxis_label = self.graphApp.get_xlab()
780        self.yaxis_label = self.graphApp.get_ylab()
781        self.xaxis_unit = self.graphApp.get_xunit()
782        self.yaxis_unit = self.graphApp.get_yunit()
[657e52c]783        self.xaxis_font = self.graphApp.get_xfont()
784        self.yaxis_font = self.graphApp.get_yfont()
[c039589]785        self.is_xtick = self.graphApp.get_xtick_check()
786        self.is_ytick = self.graphApp.get_ytick_check()
[657e52c]787        if self.is_xtick:
788            self.xaxis_tick = self.xaxis_font
789        if self.is_ytick:
790            self.yaxis_tick = self.yaxis_font
[8a687cfd]791
[c039589]792        self.xaxis(self.xaxis_label, self.xaxis_unit,
793                   self.graphApp.get_xfont(), self.graphApp.get_xcolor(),
[657e52c]794                   self.xaxis_tick)
[c039589]795        self.yaxis(self.yaxis_label, self.yaxis_unit,
[657e52c]796                   self.graphApp.get_yfont(), self.graphApp.get_ycolor(),
797                   self.yaxis_tick)
[8a687cfd]798
799        self.graphApp.Destroy()
Note: See TracBrowser for help on using the repository browser.