source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 7ce4862

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 7ce4862 was 240c805, checked in by Jae Cho <jhjcho@…>, 16 years ago

make sure everything is committed

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