source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 1f3655a

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 1f3655a was 50cbace, checked in by Gervaise Alina <gervyh@…>, 16 years ago

fixing 1 bug on plot2D model

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