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

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 0d9dae8 was 0d9dae8, checked in by Gervaise Alina <gervyh@…>, 15 years ago

add a new module containing common classes that I used often:
sans.guiframe.utils

remove event slicer and add it in sans.guicoom.events,
modified slicer for those events

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