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

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

worling on slicer panel

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