source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 0c218d9

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 0c218d9 was 216efab, checked in by Jae Cho <jhjcho@…>, 16 years ago

qmax was not defined right in circular averaging. Fixed.

  • Property mode set to 100644
File size: 18.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 pylab
15
16import danse.common.plottools
17from danse.common.plottools.PlotPanel import PlotPanel
18from danse.common.plottools.plottables import Graph,Data1D
19from sans.guicomm.events import EVT_NEW_PLOT,EVT_SLICER_PARS_UPDATE
20from sans.guicomm.events import EVT_SLICER_PARS
21from sans.guicomm.events import StatusEvent ,NewPlotEvent,SlicerEvent
22from sans.guiframe.utils import PanelMenu
23from binder import BindArtist
24from Plotter1D import ModelPanel1D
25(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
26
27
28
29DEFAULT_QMAX = 0.05
30DEFAULT_QSTEP = 0.001
31DEFAULT_BEAM = 0.005
32BIN_WIDTH = 1.0
33
34
35
36
37class ModelPanel2D( ModelPanel1D):
38    """
39        Plot panel for use with the GUI manager
40    """
41   
42    ## Internal name for the AUI manager
43    window_name = "plotpanel"
44    ## Title to appear on top of the window
45    window_caption = "Plot Panel"
46    ## Flag to tell the GUI manager that this panel is not
47    #  tied to any perspective
48    ALWAYS_ON = True
49    ## Group ID
50    group_id = None
51   
52   
53    def __init__(self, parent, id = -1,data2d=None, color = None,\
54        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
55        """
56            Initialize the panel
57        """
58        ModelPanel1D.__init__(self, parent, id = id, style = style, **kwargs)
59       
60        ## Reference to the parent window
61        self.parent = parent
62        ## Plottables
63        self.plots = {}
64        self.data2D= data2d
65        self.data =data2d.data
66        ## Unique ID (from gui_manager)
67        self.uid = None
68       
69        ## Action IDs for internal call-backs
70        self.action_ids = {}
71        self.connect = BindArtist(self.subplot.figure)
72       
73        # Beam stop
74        self.beamstop_radius = DEFAULT_BEAM
75       
76        self.slicer_z = 5
77        self.slicer = None
78        #self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
79        self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
80        self.axes_frozen = False
81       
82        self.panel_slicer=None
83        #self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
84        ## Graph       
85        self.graph = Graph()
86        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
87        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
88        self.graph.render(self)
89        #self.Bind(boxSum.EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PARS)
90        self.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
91        self.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
92       
93       
94    def _onEVT_SLICER_PARS(self, event):
95        #print "paramaters entered on slicer panel", event.type, event.params
96        self.slicer.set_params(event.params)
97        from sans.guicomm.events import SlicerPanelEvent
98        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
99       
100       
101    def _onEVT_SLICER_PANEL(self, event):
102        #print "box move plotter2D", event.type, event.params
103        self.panel_slicer.set_slicer(event.type, event.params)
104        from sans.guicomm.events import SlicerPanelEvent
105        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer)) 
106       
107    def _onEVT_1DREPLOT(self, event):
108        """
109            Data is ready to be displayed
110           
111            #TODO: this name should be changed to something more appropriate
112            # Don't forget that changing this name will mean changing code
113            # in plotting.py
114             
115            @param event: data event
116        """
117             
118        self.data2D= event.plot
119        self.data =event.plot.data
120        #TODO: Check for existence of plot attribute
121
122        # Check whether this is a replot. If we ask for a replot
123        # and the plottable no longer exists, ignore the event.
124        if hasattr(event, "update") and event.update==True \
125            and event.plot.name not in self.plots.keys():
126            return
127       
128        if hasattr(event, "reset"):
129            self._reset()
130        is_new = True
131        if event.plot.name in self.plots.keys():
132            # Check whether the class of plottable changed
133            if not event.plot.__class__==self.plots[event.plot.name].__class__:
134                #overwrite a plottable using the same name
135                self.graph.delete(self.plots[event.plot.name])
136            else:
137                # plottable is already draw on the panel
138                is_new = False
139           
140        if is_new:
141            # a new plottable overwrites a plotted one  using the same id
142            for plottable in self.plots.itervalues():
143                if event.plot.id==plottable.id :
144                    self.graph.delete(plottable)
145           
146            self.plots[event.plot.name] = event.plot
147            self.graph.add(self.plots[event.plot.name])
148        else:
149            # Update the plottable with the new data
150           
151            #TODO: we should have a method to do this,
152            #      something along the lines of:
153            #      plottable1.update_data_from_plottable(plottable2)
154           
155            self.plots[event.plot.name].xmin = event.plot.xmin
156            self.plots[event.plot.name].xmax = event.plot.xmax
157            self.plots[event.plot.name].ymin = event.plot.ymin
158            self.plots[event.plot.name].ymax = event.plot.ymax
159            self.plots[event.plot.name].data = event.plot.data
160            self.plots[event.plot.name].err_data = event.plot.err_data
161            # update qmax with the new xmax of data plotted
162            self.qmax= event.plot.xmax
163           
164        self.slicer= None
165       
166        # Check axis labels
167        #TODO: Should re-factor this
168        #if event.plot._xunit != self.graph.prop["xunit"]:
169        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
170           
171        #if event.plot._yunit != self.graph.prop["yunit"]:
172        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
173        self.graph.title(self.data2D.name)
174        self.graph.render(self)
175        self.subplot.figure.canvas.draw_idle()
176
177
178    def onContextMenu(self, event):
179        """
180            2D plot context menu
181            @param event: wx context event
182        """
183       
184        #slicerpop = wx.Menu()
185        slicerpop = PanelMenu()
186        slicerpop.set_plots(self.plots)
187        slicerpop.set_graph(self.graph)
188   
189        item_list = self.parent.get_context_menu(self.graph)
190        if (not item_list==None) and (not len(item_list)==0):
191               
192                for item in item_list:
193                    try:
194                        id = wx.NewId()
195                        slicerpop.Append(id, item[0], item[1])
196                        wx.EVT_MENU(self, id, item[2])
197                    except:
198                        pass
199                        #print sys.exc_value
200                        #print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
201       
202        slicerpop.AppendSeparator()
203       
204        id = wx.NewId()
205        slicerpop.Append(id, '&Perform circular average')
206        wx.EVT_MENU(self, id, self.onCircular) 
207       
208        id = wx.NewId()
209        slicerpop.Append(id, '&Sector [Q view]')
210        wx.EVT_MENU(self, id, self.onSectorQ) 
211       
212        id = wx.NewId()
213        slicerpop.Append(id, '&Annulus [Phi view ]')
214        wx.EVT_MENU(self, id, self.onSectorPhi) 
215       
216        id = wx.NewId()
217        slicerpop.Append(id, '&Box Sum')
218        wx.EVT_MENU(self, id, self.onBoxSum) 
219       
220        id = wx.NewId()
221        slicerpop.Append(id, '&Box averaging in Qx')
222        wx.EVT_MENU(self, id, self.onBoxavgX) 
223       
224        id = wx.NewId()
225        slicerpop.Append(id, '&Box averaging in Qy')
226        wx.EVT_MENU(self, id, self.onBoxavgY) 
227        if self.slicer !=None:
228            id = wx.NewId()
229            slicerpop.Append(id, '&Clear slicer')
230            wx.EVT_MENU(self, id,  self.onClearSlicer) 
231       
232            id = wx.NewId()
233            slicerpop.Append(id, '&Edit Slicer Parameters')
234            wx.EVT_MENU(self, id, self._onEditSlicer) 
235        slicerpop.AppendSeparator() 
236           
237        id = wx.NewId()
238        slicerpop.Append(id, '&Save image')
239        wx.EVT_MENU(self, id, self.onSaveImage) 
240       
241        # Option to save the data displayed
242        id = wx.NewId()
243        slicerpop.Append(id,'&Printer setup', 'Set image size')
244        wx.EVT_MENU(self, id, self.onPrinterSetup)
245       
246        id = wx.NewId()
247        slicerpop.Append(id,'&Printer Preview', 'Set image size')
248        wx.EVT_MENU(self, id, self.onPrinterPreview)
249   
250        id = wx.NewId()
251        slicerpop.Append(id,'&Print image', 'Print image ')
252        wx.EVT_MENU(self, id, self.onPrint)
253        slicerpop.AppendSeparator()
254        id = wx.NewId()
255        slicerpop.Append(id, '&Toggle Linear/Log scale')
256        wx.EVT_MENU(self, id, self._onToggleScale) 
257                 
258     
259        pos = event.GetPosition()
260        pos = self.ScreenToClient(pos)
261        self.PopupMenu(slicerpop, pos)
262       
263   
264       
265       
266    def get_corrected_data(self):
267        # Protect against empty data set
268        if self.data == None:
269            return None
270        import copy
271        output = copy.deepcopy(self.data)
272        return output
273    def freeze_axes(self):
274        self.axes_frozen = True
275       
276    def thaw_axes(self):
277        self.axes_frozen = False
278       
279    def onMouseMotion(self,event):
280        pass
281    def onWheel(self, event):
282        pass   
283    def update(self, draw=True):
284        """
285            Respond to changes in the model by recalculating the
286            profiles and resetting the widgets.
287        """
288        #self.slicer.update()
289        self.draw()
290       
291       
292    def _getEmptySlicerEvent(self):
293        return SlicerEvent(type=None,
294                           params=None,
295                           obj_class=None)
296    def _onEVT_INTERNAL(self, event):
297        """
298            I don't understand why Unbind followed by a Bind
299            using a modified self.slicer doesn't work.
300            For now, I post a clear event followed by
301            a new slicer event...
302        """
303        self._setSlicer(event.slicer)
304           
305    def _setSlicer(self, slicer):
306        # Clear current slicer
307        #printEVT("Plotter2D._setSlicer %s" % slicer)
308       
309        if not self.slicer == None: 
310            self.slicer.clear()           
311           
312        self.slicer_z += 1
313        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
314        #print "come here"
315        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
316        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
317       
318        self.update()
319        self.slicer.update()
320       
321        # Post slicer event
322        event = self._getEmptySlicerEvent()
323        event.type = self.slicer.__class__.__name__
324       
325        event.obj_class = self.slicer.__class__
326        event.params = self.slicer.get_params()
327        #print "Plotter2D: event.type",event.type,event.params, self.parent
328       
329        #wx.PostEvent(self.parent, event)
330        wx.PostEvent(self, event)
331
332    def onCircular(self, event):
333        """
334            perform circular averaging on Data2D
335        """
336       
337        from DataLoader.manipulations import CircularAverage
338        import math
339        self.qmax= max(math.fabs(self.data2D.xmax),math.fabs(self.data2D.xmin))
340        self.ymax=max(math.fabs(self.data2D.ymax),math.fabs(self.data2D.ymin))
341        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.ymax,2)) 
342        #print "radius?",self.radius
343        # bin_width = self.qmax -self.qmin/nbins
344        #nbins= 30
345        bin_width = (self.qmax +self.qmax)/30
346       
347        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
348       
349        circ = Circle(self.data2D)
350        from sans.guiframe.dataFitting import Data1D
351        if hasattr(circ,"dxl"):
352            dxl= circ.dxl
353        else:
354            dxl= None
355        if hasattr(circ,"dxw"):
356            dxw= circ.dxw
357        else:
358            dxw= None
359       
360        new_plot = Data1D(x=circ.x,y=circ.y,dy=circ.dy,dxl=dxl,dxw=dxw)
361        new_plot.name = "Circ avg "+ self.data2D.name
362        new_plot.source=self.data2D.source
363        #new_plot.info=self.data2D.info
364        new_plot.interactive = True
365        #print "loader output.detector",output.source
366        new_plot.detector =self.data2D.detector
367       
368        # If the data file does not tell us what the axes are, just assume...
369        new_plot.xaxis("\\rm{q}","A^{-1}")
370        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
371        new_plot.group_id = "Circ avg "+ self.data2D.name
372        self.scale = 'log'
373       
374        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
375       
376    def _onEditSlicer(self, event):
377        if self.slicer !=None:
378            from SlicerParameters import SlicerParameterPanel
379            dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
380            dialog.set_slicer(self.slicer.__class__.__name__,
381                            self.slicer.get_params())
382            if dialog.ShowModal() == wx.ID_OK:
383                dialog.Destroy() 
384       
385    def onSectorQ(self, event):
386        """
387            Perform sector averaging on Q
388        """
389        #print "onsector self.data2Dxmax",self.data2D.xmax, self.parent
390        from SectorSlicer import SectorInteractor
391        self.onClearSlicer(event)
392        #wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
393        wx.PostEvent(self, InternalEvent(slicer= SectorInteractor))
394       
395    def onSectorPhi(self, event):
396        """
397            Perform sector averaging on Phi
398        """
399        from AnnulusSlicer import AnnulusInteractor
400        self.onClearSlicer(event)
401        #wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
402        wx.PostEvent(self, InternalEvent(slicer= AnnulusInteractor))
403       
404    def onBoxSum(self,event):
405        from boxSum import BoxSum
406        self.onClearSlicer(event)
407        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
408        if not self.slicer == None: 
409            self.slicer.clear()             
410        self.slicer_z += 1
411        self.slicer =  BoxSum(self, self.subplot, zorder=self.slicer_z)
412        #print "come here"
413        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
414        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
415       
416        self.update()
417        self.slicer.update()
418       
419        # Post slicer event
420        event = self._getEmptySlicerEvent()
421        event.type = self.slicer.__class__.__name__
422       
423       
424        event.obj_class = self.slicer.__class__
425        event.params = self.slicer.get_params()
426        #print "Plotter2D: event.type",event.type,event.params, self.parent
427       
428        from slicerpanel import SlicerPanel
429        new_panel = SlicerPanel(parent= self.parent,id= -1,base= self,type=event.type,
430                                 params=event.params, style=wx.RAISED_BORDER)
431        #new_panel.set_slicer(self.slicer.__class__.__name__,
432        new_panel.window_caption=self.slicer.__class__.__name__+" "+ str(self.data2D.name)
433       
434        self.panel_slicer= new_panel
435       
436        wx.PostEvent(self.panel_slicer, event)
437        from sans.guicomm.events import SlicerPanelEvent
438        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
439        #print "finish box sum"
440       
441    def onBoxavgX(self,event):
442        from boxSlicer import BoxInteractorX
443        self.onClearSlicer(event)
444        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
445        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorX))
446       
447       
448    def onBoxavgY(self,event):
449        from boxSlicer import BoxInteractorY
450        self.onClearSlicer(event)
451        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorY))
452        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
453       
454    def onClearSlicer(self, event):
455        """
456            Clear the slicer on the plot
457        """
458        if not self.slicer==None:
459            self.slicer.clear()
460            self.subplot.figure.canvas.draw()
461            self.slicer = None
462       
463            # Post slicer None event
464            event = self._getEmptySlicerEvent()
465            #wx.PostEvent(self.parent, event)
466            wx.PostEvent(self, event)
467         
468    def _onEditDetector(self, event):
469        print "on parameter"
470       
471       
472    def _onToggleScale(self, event):
473        """
474            toggle pixel scale and replot image
475        """
476        if self.scale == 'log':
477            self.scale = 'linear'
478        else:
479            self.scale = 'log'
480        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
481                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
482        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
483       
484        """     
485            #TODO: this name should be changed to something more appropriate
486            # Don't forget that changing this name will mean changing code
487            # in plotting.py
488             
489            # Update the plottable with the new data
490           
491            #TODO: we should have a method to do this,
492            #      something along the lines of:
493            #      plottable1.update_data_from_plottable(plottable2)
494           
495            self.plots[event.plot.name].xmin = event.plot.xmin
496            self.plots[event.plot.name].xmax = event.plot.xmax
497            self.plots[event.plot.name].ymin = event.plot.ymin
498            self.plots[event.plot.name].ymax = event.plot.ymax
499            self.plots[event.plot.name].data = event.plot.data
500            self.plots[event.plot.name].err_data = event.plot.err_data
501        """
Note: See TracBrowser for help on using the repository browser.