source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ ffd23b5

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 ffd23b5 was ffd23b5, checked in by Gervaise Alina <gervyh@…>, 15 years ago
  • Property mode set to 100644
File size: 16.5 KB
RevLine 
[1bf33c1]1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, University of Tennessee
9"""
10
11
12import wx
13import sys
14import danse.common.plottools
15from danse.common.plottools.PlotPanel import PlotPanel
16from danse.common.plottools.plottables import Graph,Data1D
17from sans.guicomm.events import EVT_NEW_PLOT
18from sans.guicomm.events import StatusEvent ,NewPlotEvent
19
20
21from SlicerParameters import SlicerEvent
22from binder import BindArtist
23(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
24#from SlicerParameters import SlicerEvent
25#(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
26from Plotter1D import ModelPanel1D
27DEFAULT_QMAX = 0.05
28
29DEFAULT_QSTEP = 0.001
30DEFAULT_BEAM = 0.005
[ef0c170]31BIN_WIDTH = 1.0
[1bf33c1]32import pylab
33from Plotter1D import PanelMenu
34
35class ModelPanel2D( ModelPanel1D):
36    """
37        Plot panel for use with the GUI manager
38    """
39   
40    ## Internal name for the AUI manager
41    window_name = "plotpanel"
42    ## Title to appear on top of the window
43    window_caption = "Plot Panel"
44    ## Flag to tell the GUI manager that this panel is not
45    #  tied to any perspective
46    ALWAYS_ON = True
47    ## Group ID
48    group_id = None
49   
50   
51    def __init__(self, parent, id = -1,data2d=None, color = None,\
52        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
53        """
54            Initialize the panel
55        """
56        ModelPanel1D.__init__(self, parent, id = id, style = style, **kwargs)
57       
58        ## Reference to the parent window
59        self.parent = parent
60        ## Plottables
61        self.plots = {}
62        self.data2D= data2d
[ac9a5f6]63        self.data =data2d.data
[1bf33c1]64        ## Unique ID (from gui_manager)
65        self.uid = None
66       
67        ## Action IDs for internal call-backs
68        self.action_ids = {}
69        self.connect = BindArtist(self.subplot.figure)
70       
71        # Beam stop
72        self.beamstop_radius = DEFAULT_BEAM
73        # Slicer
[c09f14a]74        """
[1bf33c1]75        if data2d.xmax==None:
76            data2d.xmax=DEFAULT_QMAX+self.qstep*0.01
77       
78        self.qmax = data2d.xmax
79        if data2d.ymax==None:
80            data2d.ymax=DEFAULT_QMAX+self.qstep*0.01
81        self.imax = data2d.ymax
[c09f14a]82        """
[1bf33c1]83        #self.radius = math.sqrt(data2d.)
[c09f14a]84        #self.qstep = DEFAULT_QSTEP
[ef0c170]85        #print "panel2D qmax",self.qmax,
86       
[c09f14a]87        #self.x = pylab.arange(-1*self.qmax, self.qmax+self.qstep*0.01, self.qstep)
88        #self.y = pylab.arange(-1*self.imax, self.imax+self.qstep*0.01, self.qstep)
[f15ed33]89        self.slicer_z = 5
[1bf33c1]90        self.slicer = None
91        self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
92        self.axes_frozen = False
93       
94       
95        ## Graph       
96        self.graph = Graph()
97        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
98        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
99        self.graph.render(self)
100 
101    def _onEVT_1DREPLOT(self, event):
102        """
103            Data is ready to be displayed
[4b91fd1]104           
105            #TODO: this name should be changed to something more appropriate
106            # Don't forget that changing this name will mean changing code
107            # in plotting.py
108             
[1bf33c1]109            @param event: data event
110        """
111        #TODO: Check for existence of plot attribute
[ab8f936]112
[1bf33c1]113        # Check whether this is a replot. If we ask for a replot
114        # and the plottable no longer exists, ignore the event.
115        if hasattr(event, "update") and event.update==True \
116            and event.plot.name not in self.plots.keys():
117            return
[ab8f936]118       
[1bf33c1]119        if hasattr(event, "reset"):
120            self._reset()
121        is_new = True
122        if event.plot.name in self.plots.keys():
123            # Check whether the class of plottable changed
124            if not event.plot.__class__==self.plots[event.plot.name].__class__:
[ab8f936]125                #overwrite a plottable using the same name
[1bf33c1]126                self.graph.delete(self.plots[event.plot.name])
127            else:
[ab8f936]128                # plottable is already draw on the panel
[1bf33c1]129                is_new = False
[ab8f936]130           
131        if is_new:
132            # a new plottable overwrites a plotted one  using the same id
133            for plottable in self.plots.itervalues():
134                if event.plot.id==plottable.id :
135                    self.graph.delete(plottable)
136           
137            self.plots[event.plot.name] = event.plot
138            self.graph.add(self.plots[event.plot.name])
139        else:
[4b91fd1]140            # Update the plottable with the new data
141           
142            #TODO: we should have a method to do this,
143            #      something along the lines of:
144            #      plottable1.update_data_from_plottable(plottable2)
145           
146            self.plots[event.plot.name].xmin = event.plot.xmin
147            self.plots[event.plot.name].xmax = event.plot.xmax
148            self.plots[event.plot.name].ymin = event.plot.ymin
149            self.plots[event.plot.name].ymax = event.plot.ymax
150            self.plots[event.plot.name].data = event.plot.data
151            self.plots[event.plot.name].err_data = event.plot.err_data
[ac9a5f6]152            # update qmax with the new xmax of data plotted
153            self.qmax= event.plot.xmax
[4b91fd1]154           
[ac9a5f6]155        self.slicer= None
[ffd23b5]156       
[1bf33c1]157        # Check axis labels
158        #TODO: Should re-factor this
159        #if event.plot._xunit != self.graph.prop["xunit"]:
160        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
[ab8f936]161           
[1bf33c1]162        #if event.plot._yunit != self.graph.prop["yunit"]:
163        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
[ab8f936]164     
[1bf33c1]165        self.graph.render(self)
166        self.subplot.figure.canvas.draw_idle()
167
168
169    def onContextMenu(self, event):
170        """
171            2D plot context menu
172            @param event: wx context event
173        """
174       
175        #slicerpop = wx.Menu()
176        slicerpop = PanelMenu()
177        slicerpop.set_plots(self.plots)
178        slicerpop.set_graph(self.graph)
179               
180        # Option to save the data displayed
181   
182        # Various plot options
183        id = wx.NewId()
184        slicerpop.Append(id,'&Save image', 'Save image as PNG')
185        wx.EVT_MENU(self, id, self.onSaveImage)
186       
187       
188        item_list = self.parent.get_context_menu(self.graph)
189        if (not item_list==None) and (not len(item_list)==0):
190                slicerpop.AppendSeparator()
191                for item in item_list:
192                    try:
193                        id = wx.NewId()
194                        slicerpop.Append(id, item[0], item[1])
195                        wx.EVT_MENU(self, id, item[2])
196                    except:
197                        print sys.exc_value
198                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
199       
200        slicerpop.AppendSeparator()
201       
202        id = wx.NewId()
203        slicerpop.Append(id, '&Perform circular average')
204        wx.EVT_MENU(self, id, self.onCircular) 
205       
206        id = wx.NewId()
[ef0c170]207        slicerpop.Append(id, '&Sector [Q view]')
[1bf33c1]208        wx.EVT_MENU(self, id, self.onSectorQ) 
209       
210        id = wx.NewId()
[ef0c170]211        slicerpop.Append(id, '&Annulus [Phi view ]')
[1bf33c1]212        wx.EVT_MENU(self, id, self.onSectorPhi) 
[92c2345]213       
[7ab9241]214        id = wx.NewId()
215        slicerpop.Append(id, '&Box Sum')
216        wx.EVT_MENU(self, id, self.onBoxSum) 
217       
[38224f10]218        id = wx.NewId()
[8a7a21b]219        slicerpop.Append(id, '&Box averaging in Qx')
220        wx.EVT_MENU(self, id, self.onBoxavgX) 
221       
222        id = wx.NewId()
223        slicerpop.Append(id, '&Box averaging in Qy')
224        wx.EVT_MENU(self, id, self.onBoxavgY) 
[92c2345]225        if self.slicer !=None:
226            id = wx.NewId()
227            slicerpop.Append(id, '&Clear slicer')
228            wx.EVT_MENU(self, id,  self.onClearSlicer) 
[1bf33c1]229       
230       
[92c2345]231            id = wx.NewId()
232            slicerpop.Append(id, '&Edit Slicer Parameters')
233            wx.EVT_MENU(self, id, self._onEditSlicer) 
[1bf33c1]234       
235        slicerpop.AppendSeparator()
236       
237        id = wx.NewId()
238        slicerpop.Append(id, '&Save image')
239        wx.EVT_MENU(self, id, self.onSaveImage) 
240     
241        id = wx.NewId()
242        slicerpop.Append(id, '&Toggle Linear/Log scale')
243        wx.EVT_MENU(self, id, self._onToggleScale) 
244         
245        pos = event.GetPosition()
246        pos = self.ScreenToClient(pos)
247        self.PopupMenu(slicerpop, pos)
[aa1b747]248       
[240c805]249   
[1bf33c1]250       
251       
252    def get_corrected_data(self):
253        # Protect against empty data set
254        if self.data == None:
255            return None
256        import copy
257        output = copy.deepcopy(self.data)
258        return output
259    def freeze_axes(self):
260        self.axes_frozen = True
261       
262    def thaw_axes(self):
263        self.axes_frozen = False
264       
265    def onMouseMotion(self,event):
266        pass
267    def onWheel(self, event):
268        pass   
269    def update(self, draw=True):
270        """
271            Respond to changes in the model by recalculating the
272            profiles and resetting the widgets.
273        """
274        #self.slicer.update()
275        self.draw()
276       
277       
278    def _getEmptySlicerEvent(self):
279        return SlicerEvent(type=None,
280                           params=None,
281                           obj_class=None)
282    def _onEVT_INTERNAL(self, event):
283        """
284            I don't understand why Unbind followed by a Bind
285            using a modified self.slicer doesn't work.
286            For now, I post a clear event followed by
287            a new slicer event...
288        """
289        self._setSlicer(event.slicer)
290           
291    def _setSlicer(self, slicer):
292        # Clear current slicer
293        #printEVT("Plotter2D._setSlicer %s" % slicer)
294       
295        if not self.slicer == None: 
296            self.slicer.clear()           
297           
298        self.slicer_z += 1
299        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
[c09f14a]300        print "come here"
[240c805]301        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
302        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
303       
[1bf33c1]304        self.update()
305        self.slicer.update()
306       
307        # Post slicer event
308        event = self._getEmptySlicerEvent()
309        event.type = self.slicer.__class__.__name__
[92c2345]310        print "event.type",event.type
[1bf33c1]311        event.obj_class = self.slicer.__class__
312        event.params = self.slicer.get_params()
[92c2345]313        try:
314            event.result= self.slicer.get_result()
315        except:
316            event.result= None
317        print "event.result", event.result
[1bf33c1]318        wx.PostEvent(self.parent, event)
319
320    def onCircular(self, event):
321        """
322            perform circular averaging on Data2D
323        """
324       
325        from DataLoader.manipulations import CircularAverage
[ef0c170]326        import math
[c09f14a]327        self.qmax= self.data2D.xmax
[ef0c170]328        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
329        print "radius?",self.radius
[c73d871]330        # bin_width = self.qmax -self.qmin/nbins
331        #nbins= 30
332        bin_width = (self.qmax +self.qmax)/30
333       
334        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
335       
[1bf33c1]336        circ = Circle(self.data2D)
337        from sans.guiframe.dataFitting import Data1D
338        if hasattr(circ,"dxl"):
339            dxl= circ.dxl
340        else:
341            dxl= None
342        if hasattr(circ,"dxw"):
343            dxw= circ.dxw
344        else:
345            dxw= None
[ef0c170]346       
[1bf33c1]347        new_plot = Data1D(x=circ.x,y=circ.y,dy=circ.dy,dxl=dxl,dxw=dxw)
348        new_plot.name = "Circ avg "+ self.data2D.name
349        new_plot.source=self.data2D.source
350        new_plot.info=self.data2D.info
351        new_plot.interactive = True
352        #print "loader output.detector",output.source
353        new_plot.detector =self.data2D.detector
354       
355        # If the data file does not tell us what the axes are, just assume...
356        new_plot.xaxis("\\rm{q}","A^{-1}")
357        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
358        new_plot.group_id = "Circ avg "+ self.data2D.name
359        self.scale = 'log'
[ef0c170]360       
[1bf33c1]361        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
[ef0c170]362       
[1bf33c1]363    def _onEditSlicer(self, event):
364        if self.slicer !=None:
365            from SlicerParameters import SlicerParameterPanel
366            dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
367            dialog.set_slicer(self.slicer.__class__.__name__,
368                            self.slicer.get_params())
369            if dialog.ShowModal() == wx.ID_OK:
370                dialog.Destroy() 
371       
372    def onSectorQ(self, event):
373        """
374            Perform sector averaging on Q
375        """
[ac9a5f6]376        print "onsector self.data2Dxmax",self.data2D.xmax
[ef0c170]377        from SectorSlicer import SectorInteractor
[1bf33c1]378        self.onClearSlicer(event)
[ef0c170]379        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
[1bf33c1]380       
381    def onSectorPhi(self, event):
382        """
383            Perform sector averaging on Phi
384        """
[ef0c170]385        from AnnulusSlicer import AnnulusInteractor
[1bf33c1]386        self.onClearSlicer(event)
[ef0c170]387        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
[1bf33c1]388       
[7ab9241]389    def onBoxSum(self,event):
390        from boxSum import BoxSum
391        self.onClearSlicer(event)
392        wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
[8a7a21b]393       
394        from slicerpanel import SlicerPanel
395        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
396       
397        from sans.guicomm.events import SlicerParameterEvent
398        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
[92c2345]399           
[8a7a21b]400    def onBoxavgX(self,event):
401        from boxSlicer import BoxInteractorX
[38224f10]402        self.onClearSlicer(event)
[8a7a21b]403        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
[aa1b747]404       
405        from slicerpanel import SlicerPanel
406        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
407       
408        from sans.guicomm.events import SlicerParameterEvent
409        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
410       
[8a7a21b]411    def onBoxavgY(self,event):
412        from boxSlicer import BoxInteractorY
413        self.onClearSlicer(event)
414        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
415       
416        from slicerpanel import SlicerPanel
417        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
[b319def8]418       
[8a7a21b]419        from sans.guicomm.events import SlicerParameterEvent
420        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
421         
[1bf33c1]422    def onClearSlicer(self, event):
423        """
424            Clear the slicer on the plot
425        """
426        if not self.slicer==None:
427            self.slicer.clear()
428            self.subplot.figure.canvas.draw()
429            self.slicer = None
430       
431            # Post slicer None event
432            event = self._getEmptySlicerEvent()
433            wx.PostEvent(self.parent, event)
434         
435    def _onEditDetector(self, event):
436        print "on parameter"
437       
438       
439    def _onToggleScale(self, event):
440        """
441            toggle pixel scale and replot image
442        """
443        if self.scale == 'log':
444            self.scale = 'linear'
445        else:
446            self.scale = 'log'
447        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
448                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
449        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
450       
[0f6d05f8]451        """     
452            #TODO: this name should be changed to something more appropriate
453            # Don't forget that changing this name will mean changing code
454            # in plotting.py
455             
456            # Update the plottable with the new data
457           
458            #TODO: we should have a method to do this,
459            #      something along the lines of:
460            #      plottable1.update_data_from_plottable(plottable2)
461           
462            self.plots[event.plot.name].xmin = event.plot.xmin
463            self.plots[event.plot.name].xmax = event.plot.xmax
464            self.plots[event.plot.name].ymin = event.plot.ymin
465            self.plots[event.plot.name].ymax = event.plot.ymax
466            self.plots[event.plot.name].data = event.plot.data
467            self.plots[event.plot.name].err_data = event.plot.err_data
468        """
Note: See TracBrowser for help on using the repository browser.