source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 8a7a21b

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 8a7a21b was 8a7a21b, checked in by Gervaise Alina <gervyh@…>, 15 years ago

working on slicer event for plotter2D

  • Property mode set to 100644
File size: 16.5 KB
Line 
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
31BIN_WIDTH = 1.0
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
63        self.data =data2d.data
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
74        """
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
82        """
83        #self.radius = math.sqrt(data2d.)
84        #self.qstep = DEFAULT_QSTEP
85        #print "panel2D qmax",self.qmax,
86       
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)
89        self.slicer_z = 5
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
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             
109            @param event: data event
110        """
111        #TODO: Check for existence of plot attribute
112
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
118       
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__:
125                #overwrite a plottable using the same name
126                self.graph.delete(self.plots[event.plot.name])
127            else:
128                # plottable is already draw on the panel
129                is_new = False
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:
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
152            # update qmax with the new xmax of data plotted
153            self.qmax= event.plot.xmax
154           
155        self.slicer= None
156        # Check axis labels
157        #TODO: Should re-factor this
158        #if event.plot._xunit != self.graph.prop["xunit"]:
159        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
160           
161        #if event.plot._yunit != self.graph.prop["yunit"]:
162        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
163     
164        self.graph.render(self)
165        self.subplot.figure.canvas.draw_idle()
166
167
168    def onContextMenu(self, event):
169        """
170            2D plot context menu
171            @param event: wx context event
172        """
173       
174        #slicerpop = wx.Menu()
175        slicerpop = PanelMenu()
176        slicerpop.set_plots(self.plots)
177        slicerpop.set_graph(self.graph)
178               
179        # Option to save the data displayed
180   
181        # Various plot options
182        id = wx.NewId()
183        slicerpop.Append(id,'&Save image', 'Save image as PNG')
184        wx.EVT_MENU(self, id, self.onSaveImage)
185       
186       
187        item_list = self.parent.get_context_menu(self.graph)
188        if (not item_list==None) and (not len(item_list)==0):
189                slicerpop.AppendSeparator()
190                for item in item_list:
191                    try:
192                        id = wx.NewId()
193                        slicerpop.Append(id, item[0], item[1])
194                        wx.EVT_MENU(self, id, item[2])
195                    except:
196                        print sys.exc_value
197                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
198       
199        slicerpop.AppendSeparator()
200       
201        id = wx.NewId()
202        slicerpop.Append(id, '&Perform circular average')
203        wx.EVT_MENU(self, id, self.onCircular) 
204       
205        id = wx.NewId()
206        slicerpop.Append(id, '&Sector [Q view]')
207        wx.EVT_MENU(self, id, self.onSectorQ) 
208       
209        id = wx.NewId()
210        slicerpop.Append(id, '&Annulus [Phi view ]')
211        wx.EVT_MENU(self, id, self.onSectorPhi) 
212       
213        id = wx.NewId()
214        slicerpop.Append(id, '&Box Sum')
215        wx.EVT_MENU(self, id, self.onBoxSum) 
216       
217        id = wx.NewId()
218        slicerpop.Append(id, '&Box averaging in Qx')
219        wx.EVT_MENU(self, id, self.onBoxavgX) 
220       
221        id = wx.NewId()
222        slicerpop.Append(id, '&Box averaging in Qy')
223        wx.EVT_MENU(self, id, self.onBoxavgY) 
224        if self.slicer !=None:
225            id = wx.NewId()
226            slicerpop.Append(id, '&Clear slicer')
227            wx.EVT_MENU(self, id,  self.onClearSlicer) 
228       
229       
230            id = wx.NewId()
231            slicerpop.Append(id, '&Edit Slicer Parameters')
232            wx.EVT_MENU(self, id, self._onEditSlicer) 
233       
234        slicerpop.AppendSeparator()
235       
236        id = wx.NewId()
237        slicerpop.Append(id, '&Save image')
238        wx.EVT_MENU(self, id, self.onSaveImage) 
239     
240        id = wx.NewId()
241        slicerpop.Append(id, '&Toggle Linear/Log scale')
242        wx.EVT_MENU(self, id, self._onToggleScale) 
243         
244        pos = event.GetPosition()
245        pos = self.ScreenToClient(pos)
246        self.PopupMenu(slicerpop, pos)
247       
248   
249       
250       
251    def get_corrected_data(self):
252        # Protect against empty data set
253        if self.data == None:
254            return None
255        import copy
256        output = copy.deepcopy(self.data)
257        return output
258    def freeze_axes(self):
259        self.axes_frozen = True
260       
261    def thaw_axes(self):
262        self.axes_frozen = False
263       
264    def onMouseMotion(self,event):
265        pass
266    def onWheel(self, event):
267        pass   
268    def update(self, draw=True):
269        """
270            Respond to changes in the model by recalculating the
271            profiles and resetting the widgets.
272        """
273        #self.slicer.update()
274        self.draw()
275       
276       
277    def _getEmptySlicerEvent(self):
278        return SlicerEvent(type=None,
279                           params=None,
280                           obj_class=None)
281    def _onEVT_INTERNAL(self, event):
282        """
283            I don't understand why Unbind followed by a Bind
284            using a modified self.slicer doesn't work.
285            For now, I post a clear event followed by
286            a new slicer event...
287        """
288        self._setSlicer(event.slicer)
289           
290    def _setSlicer(self, slicer):
291        # Clear current slicer
292        #printEVT("Plotter2D._setSlicer %s" % slicer)
293       
294        if not self.slicer == None: 
295            self.slicer.clear()           
296           
297        self.slicer_z += 1
298        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
299        print "come here"
300        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
301        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
302       
303        self.update()
304        self.slicer.update()
305       
306        # Post slicer event
307        event = self._getEmptySlicerEvent()
308        event.type = self.slicer.__class__.__name__
309        print "event.type",event.type
310        event.obj_class = self.slicer.__class__
311        event.params = self.slicer.get_params()
312        try:
313            event.result= self.slicer.get_result()
314        except:
315            event.result= None
316        print "event.result", event.result
317        wx.PostEvent(self.parent, event)
318
319    def onCircular(self, event):
320        """
321            perform circular averaging on Data2D
322        """
323       
324        from DataLoader.manipulations import CircularAverage
325        import math
326        self.qmax= self.data2D.xmax
327        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
328        print "radius?",self.radius
329        # bin_width = self.qmax -self.qmin/nbins
330        #nbins= 30
331        bin_width = (self.qmax +self.qmax)/30
332       
333        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
334       
335        circ = Circle(self.data2D)
336        from sans.guiframe.dataFitting import Data1D
337        if hasattr(circ,"dxl"):
338            dxl= circ.dxl
339        else:
340            dxl= None
341        if hasattr(circ,"dxw"):
342            dxw= circ.dxw
343        else:
344            dxw= None
345       
346        new_plot = Data1D(x=circ.x,y=circ.y,dy=circ.dy,dxl=dxl,dxw=dxw)
347        new_plot.name = "Circ avg "+ self.data2D.name
348        new_plot.source=self.data2D.source
349        new_plot.info=self.data2D.info
350        new_plot.interactive = True
351        #print "loader output.detector",output.source
352        new_plot.detector =self.data2D.detector
353       
354        # If the data file does not tell us what the axes are, just assume...
355        new_plot.xaxis("\\rm{q}","A^{-1}")
356        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
357        new_plot.group_id = "Circ avg "+ self.data2D.name
358        self.scale = 'log'
359       
360        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
361       
362    def _onEditSlicer(self, event):
363        if self.slicer !=None:
364            from SlicerParameters import SlicerParameterPanel
365            dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
366            dialog.set_slicer(self.slicer.__class__.__name__,
367                            self.slicer.get_params())
368            if dialog.ShowModal() == wx.ID_OK:
369                dialog.Destroy() 
370       
371    def onSectorQ(self, event):
372        """
373            Perform sector averaging on Q
374        """
375        print "onsector self.data2Dxmax",self.data2D.xmax
376        from SectorSlicer import SectorInteractor
377        self.onClearSlicer(event)
378        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
379       
380    def onSectorPhi(self, event):
381        """
382            Perform sector averaging on Phi
383        """
384        from AnnulusSlicer import AnnulusInteractor
385        self.onClearSlicer(event)
386        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
387       
388    def onBoxSum(self,event):
389        from boxSum import BoxSum
390        self.onClearSlicer(event)
391        wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
392       
393        from slicerpanel import SlicerPanel
394        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
395       
396        from sans.guicomm.events import SlicerParameterEvent
397        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
398           
399    def onBoxavgX(self,event):
400        from boxSlicer import BoxInteractorX
401        self.onClearSlicer(event)
402        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
403       
404        from slicerpanel import SlicerPanel
405        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
406       
407        from sans.guicomm.events import SlicerParameterEvent
408        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
409       
410    def onBoxavgY(self,event):
411        from boxSlicer import BoxInteractorY
412        self.onClearSlicer(event)
413        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
414       
415        from slicerpanel import SlicerPanel
416        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
417       
418        from sans.guicomm.events import SlicerParameterEvent
419        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
420         
421    def onClearSlicer(self, event):
422        """
423            Clear the slicer on the plot
424        """
425        if not self.slicer==None:
426            self.slicer.clear()
427            self.subplot.figure.canvas.draw()
428            self.slicer = None
429       
430            # Post slicer None event
431            event = self._getEmptySlicerEvent()
432            wx.PostEvent(self.parent, event)
433         
434    def _onEditDetector(self, event):
435        print "on parameter"
436       
437       
438    def _onToggleScale(self, event):
439        """
440            toggle pixel scale and replot image
441        """
442        if self.scale == 'log':
443            self.scale = 'linear'
444        else:
445            self.scale = 'log'
446        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
447                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
448        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
449       
450        """     
451            #TODO: this name should be changed to something more appropriate
452            # Don't forget that changing this name will mean changing code
453            # in plotting.py
454             
455            # Update the plottable with the new data
456           
457            #TODO: we should have a method to do this,
458            #      something along the lines of:
459            #      plottable1.update_data_from_plottable(plottable2)
460           
461            self.plots[event.plot.name].xmin = event.plot.xmin
462            self.plots[event.plot.name].xmax = event.plot.xmax
463            self.plots[event.plot.name].ymin = event.plot.ymin
464            self.plots[event.plot.name].ymax = event.plot.ymax
465            self.plots[event.plot.name].data = event.plot.data
466            self.plots[event.plot.name].err_data = event.plot.err_data
467        """
Note: See TracBrowser for help on using the repository browser.