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

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 a5aaec9 was 92c2345, checked in by Gervaise Alina <gervyh@…>, 16 years ago

working on boxslicer

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