source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 9c648c7

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 9c648c7 was 54cc36a, checked in by Gervaise Alina <gervyh@…>, 16 years ago

working on boxsum

  • 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       
91    def _onEVT_SLICER_PARS(self, event):
92        print "box move plotter2D", event.type, event.params
93        self.panel_slicer.set_slicer(event.type, event.params)
94        from sans.guicomm.events import SlicerPanelEvent
95        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
96    def _onEVT_1DREPLOT(self, event):
97        """
98            Data is ready to be displayed
99           
100            #TODO: this name should be changed to something more appropriate
101            # Don't forget that changing this name will mean changing code
102            # in plotting.py
103             
104            @param event: data event
105        """
106        #TODO: Check for existence of plot attribute
107
108        # Check whether this is a replot. If we ask for a replot
109        # and the plottable no longer exists, ignore the event.
110        if hasattr(event, "update") and event.update==True \
111            and event.plot.name not in self.plots.keys():
112            return
113       
114        if hasattr(event, "reset"):
115            self._reset()
116        is_new = True
117        if event.plot.name in self.plots.keys():
118            # Check whether the class of plottable changed
119            if not event.plot.__class__==self.plots[event.plot.name].__class__:
120                #overwrite a plottable using the same name
121                self.graph.delete(self.plots[event.plot.name])
122            else:
123                # plottable is already draw on the panel
124                is_new = False
125           
126        if is_new:
127            # a new plottable overwrites a plotted one  using the same id
128            for plottable in self.plots.itervalues():
129                if event.plot.id==plottable.id :
130                    self.graph.delete(plottable)
131           
132            self.plots[event.plot.name] = event.plot
133            self.graph.add(self.plots[event.plot.name])
134        else:
135            # Update the plottable with the new data
136           
137            #TODO: we should have a method to do this,
138            #      something along the lines of:
139            #      plottable1.update_data_from_plottable(plottable2)
140           
141            self.plots[event.plot.name].xmin = event.plot.xmin
142            self.plots[event.plot.name].xmax = event.plot.xmax
143            self.plots[event.plot.name].ymin = event.plot.ymin
144            self.plots[event.plot.name].ymax = event.plot.ymax
145            self.plots[event.plot.name].data = event.plot.data
146            self.plots[event.plot.name].err_data = event.plot.err_data
147            # update qmax with the new xmax of data plotted
148            self.qmax= event.plot.xmax
149           
150        self.slicer= None
151       
152        # Check axis labels
153        #TODO: Should re-factor this
154        #if event.plot._xunit != self.graph.prop["xunit"]:
155        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
156           
157        #if event.plot._yunit != self.graph.prop["yunit"]:
158        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
159     
160        self.graph.render(self)
161        self.subplot.figure.canvas.draw_idle()
162
163
164    def onContextMenu(self, event):
165        """
166            2D plot context menu
167            @param event: wx context event
168        """
169       
170        #slicerpop = wx.Menu()
171        slicerpop = PanelMenu()
172        slicerpop.set_plots(self.plots)
173        slicerpop.set_graph(self.graph)
174               
175        # Option to save the data displayed
176   
177        # Various plot options
178        id = wx.NewId()
179        slicerpop.Append(id,'&Save image', 'Save image as PNG')
180        wx.EVT_MENU(self, id, self.onSaveImage)
181       
182       
183        item_list = self.parent.get_context_menu(self.graph)
184        if (not item_list==None) and (not len(item_list)==0):
185                slicerpop.AppendSeparator()
186                for item in item_list:
187                    try:
188                        id = wx.NewId()
189                        slicerpop.Append(id, item[0], item[1])
190                        wx.EVT_MENU(self, id, item[2])
191                    except:
192                        print sys.exc_value
193                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
194       
195        slicerpop.AppendSeparator()
196       
197        id = wx.NewId()
198        slicerpop.Append(id, '&Perform circular average')
199        wx.EVT_MENU(self, id, self.onCircular) 
200       
201        id = wx.NewId()
202        slicerpop.Append(id, '&Sector [Q view]')
203        wx.EVT_MENU(self, id, self.onSectorQ) 
204       
205        id = wx.NewId()
206        slicerpop.Append(id, '&Annulus [Phi view ]')
207        wx.EVT_MENU(self, id, self.onSectorPhi) 
208       
209        id = wx.NewId()
210        slicerpop.Append(id, '&Box Sum')
211        wx.EVT_MENU(self, id, self.onBoxSum) 
212       
213        id = wx.NewId()
214        slicerpop.Append(id, '&Box averaging in Qx')
215        wx.EVT_MENU(self, id, self.onBoxavgX) 
216       
217        id = wx.NewId()
218        slicerpop.Append(id, '&Box averaging in Qy')
219        wx.EVT_MENU(self, id, self.onBoxavgY) 
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       
306        event.obj_class = self.slicer.__class__
307        event.params = self.slicer.get_params()
308        print "Plotter2D: event.type",event.type,event.params, self.parent
309       
310        wx.PostEvent(self.parent, event)
311
312    def onCircular(self, event):
313        """
314            perform circular averaging on Data2D
315        """
316       
317        from DataLoader.manipulations import CircularAverage
318        import math
319        self.qmax= self.data2D.xmax
320        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
321        print "radius?",self.radius
322        # bin_width = self.qmax -self.qmin/nbins
323        #nbins= 30
324        bin_width = (self.qmax +self.qmax)/30
325       
326        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
327       
328        circ = Circle(self.data2D)
329        from sans.guiframe.dataFitting import Data1D
330        if hasattr(circ,"dxl"):
331            dxl= circ.dxl
332        else:
333            dxl= None
334        if hasattr(circ,"dxw"):
335            dxw= circ.dxw
336        else:
337            dxw= None
338       
339        new_plot = Data1D(x=circ.x,y=circ.y,dy=circ.dy,dxl=dxl,dxw=dxw)
340        new_plot.name = "Circ avg "+ self.data2D.name
341        new_plot.source=self.data2D.source
342        new_plot.info=self.data2D.info
343        new_plot.interactive = True
344        #print "loader output.detector",output.source
345        new_plot.detector =self.data2D.detector
346       
347        # If the data file does not tell us what the axes are, just assume...
348        new_plot.xaxis("\\rm{q}","A^{-1}")
349        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
350        new_plot.group_id = "Circ avg "+ self.data2D.name
351        self.scale = 'log'
352       
353        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
354       
355    def _onEditSlicer(self, event):
356        if self.slicer !=None:
357            from SlicerParameters import SlicerParameterPanel
358            dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
359            dialog.set_slicer(self.slicer.__class__.__name__,
360                            self.slicer.get_params())
361            if dialog.ShowModal() == wx.ID_OK:
362                dialog.Destroy() 
363       
364    def onSectorQ(self, event):
365        """
366            Perform sector averaging on Q
367        """
368        print "onsector self.data2Dxmax",self.data2D.xmax
369        from SectorSlicer import SectorInteractor
370        self.onClearSlicer(event)
371        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
372       
373    def onSectorPhi(self, event):
374        """
375            Perform sector averaging on Phi
376        """
377        from AnnulusSlicer import AnnulusInteractor
378        self.onClearSlicer(event)
379        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
380       
381    def onBoxSum(self,event):
382        from boxSum import BoxSum
383        self.onClearSlicer(event)
384        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
385        if not self.slicer == None: 
386            self.slicer.clear()             
387        self.slicer_z += 1
388        self.slicer =  BoxSum(self, self.subplot, zorder=self.slicer_z)
389        print "come here"
390        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
391        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
392       
393        self.update()
394        self.slicer.update()
395       
396        # Post slicer event
397        event = self._getEmptySlicerEvent()
398        event.type = self.slicer.__class__.__name__
399       
400       
401        event.obj_class = self.slicer.__class__
402        event.params = self.slicer.get_params()
403        print "Plotter2D: event.type",event.type,event.params, self.parent
404       
405        from slicerpanel import SlicerPanel
406        new_panel = SlicerPanel(parent= self.parent,id= -1,type=event.type,
407                                 params=event.params, style=wx.RAISED_BORDER)
408        #new_panel.set_slicer(self.slicer.__class__.__name__,
409        new_panel.window_caption=self.slicer.__class__.__name__+" "+ str(self.data2D.name)
410       
411        self.panel_slicer= new_panel
412       
413        wx.PostEvent(self.panel_slicer, event)
414        from sans.guicomm.events import SlicerPanelEvent
415        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
416        print "finish box sum"
417       
418    def onBoxavgX(self,event):
419        from boxSlicer import BoxInteractorX
420        self.onClearSlicer(event)
421        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
422       
423        from slicerpanel import SlicerPanel
424        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
425       
426        from sans.guicomm.events import SlicerPanelEvent
427        wx.PostEvent(self.parent, SlicerPanelEvent (panel= new_panel))
428       
429    def onBoxavgY(self,event):
430        from boxSlicer import BoxInteractorY
431        self.onClearSlicer(event)
432        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
433       
434        from slicerpanel import SlicerPanel
435        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
436       
437        from sans.guicomm.events import SlicerParameterEvent
438        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
439         
440    def onClearSlicer(self, event):
441        """
442            Clear the slicer on the plot
443        """
444        if not self.slicer==None:
445            self.slicer.clear()
446            self.subplot.figure.canvas.draw()
447            self.slicer = None
448       
449            # Post slicer None event
450            event = self._getEmptySlicerEvent()
451            wx.PostEvent(self.parent, event)
452         
453    def _onEditDetector(self, event):
454        print "on parameter"
455       
456       
457    def _onToggleScale(self, event):
458        """
459            toggle pixel scale and replot image
460        """
461        if self.scale == 'log':
462            self.scale = 'linear'
463        else:
464            self.scale = 'log'
465        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
466                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
467        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
468       
469        """     
470            #TODO: this name should be changed to something more appropriate
471            # Don't forget that changing this name will mean changing code
472            # in plotting.py
473             
474            # Update the plottable with the new data
475           
476            #TODO: we should have a method to do this,
477            #      something along the lines of:
478            #      plottable1.update_data_from_plottable(plottable2)
479           
480            self.plots[event.plot.name].xmin = event.plot.xmin
481            self.plots[event.plot.name].xmax = event.plot.xmax
482            self.plots[event.plot.name].ymin = event.plot.ymin
483            self.plots[event.plot.name].ymax = event.plot.ymax
484            self.plots[event.plot.name].data = event.plot.data
485            self.plots[event.plot.name].err_data = event.plot.err_data
486        """
Note: See TracBrowser for help on using the repository browser.