source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 50cbace

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 50cbace 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
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
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
[ef0c170]31BIN_WIDTH = 1.0
[1bf33c1]32import pylab
33from Plotter1D import PanelMenu
[54cc36a]34#import boxSum
35from sans.guicomm.events import EVT_SLICER_PARS_UPDATE
[1bf33c1]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
[ac9a5f6]64        self.data =data2d.data
[1bf33c1]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
[54cc36a]74       
[f15ed33]75        self.slicer_z = 5
[1bf33c1]76        self.slicer = None
77        self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
78        self.axes_frozen = False
79       
[54cc36a]80        self.panel_slicer=None
81        #self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
[1bf33c1]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)
[54cc36a]87        #self.Bind(boxSum.EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PARS)
88        self.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PARS)
[50cbace]89 
[54cc36a]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))
[1bf33c1]95    def _onEVT_1DREPLOT(self, event):
96        """
97            Data is ready to be displayed
[4b91fd1]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             
[1bf33c1]103            @param event: data event
104        """
[50cbace]105             
106        self.data2D= event.plot
107        self.data =event.plot.data
[1bf33c1]108        #TODO: Check for existence of plot attribute
[ab8f936]109
[1bf33c1]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
[ab8f936]115       
[1bf33c1]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__:
[ab8f936]122                #overwrite a plottable using the same name
[1bf33c1]123                self.graph.delete(self.plots[event.plot.name])
124            else:
[ab8f936]125                # plottable is already draw on the panel
[1bf33c1]126                is_new = False
[ab8f936]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:
[4b91fd1]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
[ac9a5f6]149            # update qmax with the new xmax of data plotted
150            self.qmax= event.plot.xmax
[4b91fd1]151           
[ac9a5f6]152        self.slicer= None
[ffd23b5]153       
[1bf33c1]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)
[ab8f936]158           
[1bf33c1]159        #if event.plot._yunit != self.graph.prop["yunit"]:
160        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
[ab8f936]161     
[1bf33c1]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()
[ef0c170]204        slicerpop.Append(id, '&Sector [Q view]')
[1bf33c1]205        wx.EVT_MENU(self, id, self.onSectorQ) 
206       
207        id = wx.NewId()
[ef0c170]208        slicerpop.Append(id, '&Annulus [Phi view ]')
[1bf33c1]209        wx.EVT_MENU(self, id, self.onSectorPhi) 
[92c2345]210       
[7ab9241]211        id = wx.NewId()
212        slicerpop.Append(id, '&Box Sum')
213        wx.EVT_MENU(self, id, self.onBoxSum) 
214       
[38224f10]215        id = wx.NewId()
[8a7a21b]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) 
[92c2345]222        if self.slicer !=None:
223            id = wx.NewId()
224            slicerpop.Append(id, '&Clear slicer')
225            wx.EVT_MENU(self, id,  self.onClearSlicer) 
[1bf33c1]226       
227       
[92c2345]228            id = wx.NewId()
229            slicerpop.Append(id, '&Edit Slicer Parameters')
230            wx.EVT_MENU(self, id, self._onEditSlicer) 
[1bf33c1]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)
[aa1b747]245       
[240c805]246   
[1bf33c1]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)
[c09f14a]297        print "come here"
[240c805]298        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
299        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
300       
[1bf33c1]301        self.update()
302        self.slicer.update()
303       
304        # Post slicer event
305        event = self._getEmptySlicerEvent()
306        event.type = self.slicer.__class__.__name__
[54cc36a]307       
[1bf33c1]308        event.obj_class = self.slicer.__class__
309        event.params = self.slicer.get_params()
[54cc36a]310        print "Plotter2D: event.type",event.type,event.params, self.parent
311       
[1bf33c1]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
[ef0c170]320        import math
[c09f14a]321        self.qmax= self.data2D.xmax
[ef0c170]322        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
323        print "radius?",self.radius
[c73d871]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       
[1bf33c1]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
[ef0c170]340       
[1bf33c1]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
[50cbace]344        #new_plot.info=self.data2D.info
[1bf33c1]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'
[ef0c170]354       
[1bf33c1]355        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
[ef0c170]356       
[1bf33c1]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        """
[ac9a5f6]370        print "onsector self.data2Dxmax",self.data2D.xmax
[ef0c170]371        from SectorSlicer import SectorInteractor
[1bf33c1]372        self.onClearSlicer(event)
[ef0c170]373        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
[1bf33c1]374       
375    def onSectorPhi(self, event):
376        """
377            Perform sector averaging on Phi
378        """
[ef0c170]379        from AnnulusSlicer import AnnulusInteractor
[1bf33c1]380        self.onClearSlicer(event)
[ef0c170]381        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
[1bf33c1]382       
[7ab9241]383    def onBoxSum(self,event):
384        from boxSum import BoxSum
385        self.onClearSlicer(event)
[54cc36a]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
[8a7a21b]406       
407        from slicerpanel import SlicerPanel
[54cc36a]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"
[8a7a21b]419       
420    def onBoxavgX(self,event):
421        from boxSlicer import BoxInteractorX
[38224f10]422        self.onClearSlicer(event)
[8a7a21b]423        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
[aa1b747]424       
425        from slicerpanel import SlicerPanel
426        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
427       
[54cc36a]428        from sans.guicomm.events import SlicerPanelEvent
429        wx.PostEvent(self.parent, SlicerPanelEvent (panel= new_panel))
[aa1b747]430       
[8a7a21b]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)
[b319def8]438       
[8a7a21b]439        from sans.guicomm.events import SlicerParameterEvent
440        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
441         
[1bf33c1]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       
[0f6d05f8]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.