source: sasview/sansguiframe/src/sans/guiframe/local_perspectives/plotting/Plotter2D.py @ b837e1f

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 b837e1f was b837e1f, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed 1d 2d mixed in 1d plot

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