source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 14d3495

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 14d3495 was 0bd125d, checked in by Gervaise Alina <gervyh@…>, 16 years ago

slicer close panel fixed and find problem on sector slicermotion

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