source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 10c43a5

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 10c43a5 was d468daa, checked in by Gervaise Alina <gervyh@…>, 16 years ago

print stament removed

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