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
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       
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)
161           
162        #if event.plot._yunit != self.graph.prop["yunit"]:
163        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
164     
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()
207        slicerpop.Append(id, '&Sector [Q view]')
208        wx.EVT_MENU(self, id, self.onSectorQ) 
209       
210        id = wx.NewId()
211        slicerpop.Append(id, '&Annulus [Phi view ]')
212        wx.EVT_MENU(self, id, self.onSectorPhi) 
213       
214        id = wx.NewId()
215        slicerpop.Append(id, '&Box Sum')
216        wx.EVT_MENU(self, id, self.onBoxSum) 
217       
218        id = wx.NewId()
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) 
225        if self.slicer !=None:
226            id = wx.NewId()
227            slicerpop.Append(id, '&Clear slicer')
228            wx.EVT_MENU(self, id,  self.onClearSlicer) 
229       
230       
231            id = wx.NewId()
232            slicerpop.Append(id, '&Edit Slicer Parameters')
233            wx.EVT_MENU(self, id, self._onEditSlicer) 
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)
248       
249   
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)
300        print "come here"
301        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
302        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
303       
304        self.update()
305        self.slicer.update()
306       
307        # Post slicer event
308        event = self._getEmptySlicerEvent()
309        event.type = self.slicer.__class__.__name__
310        print "event.type",event.type
311        event.obj_class = self.slicer.__class__
312        event.params = self.slicer.get_params()
313        try:
314            event.result= self.slicer.get_result()
315        except:
316            event.result= None
317        print "event.result", event.result
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
326        import math
327        self.qmax= self.data2D.xmax
328        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
329        print "radius?",self.radius
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       
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
346       
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'
360       
361        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
362       
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        """
376        print "onsector self.data2Dxmax",self.data2D.xmax
377        from SectorSlicer import SectorInteractor
378        self.onClearSlicer(event)
379        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
380       
381    def onSectorPhi(self, event):
382        """
383            Perform sector averaging on Phi
384        """
385        from AnnulusSlicer import AnnulusInteractor
386        self.onClearSlicer(event)
387        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
388       
389    def onBoxSum(self,event):
390        from boxSum import BoxSum
391        self.onClearSlicer(event)
392        wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
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))
399           
400    def onBoxavgX(self,event):
401        from boxSlicer import BoxInteractorX
402        self.onClearSlicer(event)
403        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
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       
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)
418       
419        from sans.guicomm.events import SlicerParameterEvent
420        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
421         
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       
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.