source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 8f584c9

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 8f584c9 was 8f584c9, checked in by Jae Cho <jhjcho@…>, 15 years ago

increased # of points on 2d circular averaged plot, and from q to Q on x-axis

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