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
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)
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))
[1bf33c1]96    def _onEVT_1DREPLOT(self, event):
97        """
98            Data is ready to be displayed
[4b91fd1]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             
[1bf33c1]104            @param event: data event
105        """
106        #TODO: Check for existence of plot attribute
[ab8f936]107
[1bf33c1]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
[ab8f936]113       
[1bf33c1]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__:
[ab8f936]120                #overwrite a plottable using the same name
[1bf33c1]121                self.graph.delete(self.plots[event.plot.name])
122            else:
[ab8f936]123                # plottable is already draw on the panel
[1bf33c1]124                is_new = False
[ab8f936]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:
[4b91fd1]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
[ac9a5f6]147            # update qmax with the new xmax of data plotted
148            self.qmax= event.plot.xmax
[4b91fd1]149           
[ac9a5f6]150        self.slicer= None
[ffd23b5]151       
[1bf33c1]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)
[ab8f936]156           
[1bf33c1]157        #if event.plot._yunit != self.graph.prop["yunit"]:
158        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
[ab8f936]159     
[1bf33c1]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()
[ef0c170]202        slicerpop.Append(id, '&Sector [Q view]')
[1bf33c1]203        wx.EVT_MENU(self, id, self.onSectorQ) 
204       
205        id = wx.NewId()
[ef0c170]206        slicerpop.Append(id, '&Annulus [Phi view ]')
[1bf33c1]207        wx.EVT_MENU(self, id, self.onSectorPhi) 
[92c2345]208       
[7ab9241]209        id = wx.NewId()
210        slicerpop.Append(id, '&Box Sum')
211        wx.EVT_MENU(self, id, self.onBoxSum) 
212       
[38224f10]213        id = wx.NewId()
[8a7a21b]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) 
[92c2345]220        if self.slicer !=None:
221            id = wx.NewId()
222            slicerpop.Append(id, '&Clear slicer')
223            wx.EVT_MENU(self, id,  self.onClearSlicer) 
[1bf33c1]224       
225       
[92c2345]226            id = wx.NewId()
227            slicerpop.Append(id, '&Edit Slicer Parameters')
228            wx.EVT_MENU(self, id, self._onEditSlicer) 
[1bf33c1]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)
[aa1b747]243       
[240c805]244   
[1bf33c1]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)
[c09f14a]295        print "come here"
[240c805]296        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
297        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
298       
[1bf33c1]299        self.update()
300        self.slicer.update()
301       
302        # Post slicer event
303        event = self._getEmptySlicerEvent()
304        event.type = self.slicer.__class__.__name__
[54cc36a]305       
[1bf33c1]306        event.obj_class = self.slicer.__class__
307        event.params = self.slicer.get_params()
[54cc36a]308        print "Plotter2D: event.type",event.type,event.params, self.parent
309       
[1bf33c1]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
[ef0c170]318        import math
[c09f14a]319        self.qmax= self.data2D.xmax
[ef0c170]320        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.qmax,2)) 
321        print "radius?",self.radius
[c73d871]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       
[1bf33c1]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
[ef0c170]338       
[1bf33c1]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'
[ef0c170]352       
[1bf33c1]353        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
[ef0c170]354       
[1bf33c1]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        """
[ac9a5f6]368        print "onsector self.data2Dxmax",self.data2D.xmax
[ef0c170]369        from SectorSlicer import SectorInteractor
[1bf33c1]370        self.onClearSlicer(event)
[ef0c170]371        wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
[1bf33c1]372       
373    def onSectorPhi(self, event):
374        """
375            Perform sector averaging on Phi
376        """
[ef0c170]377        from AnnulusSlicer import AnnulusInteractor
[1bf33c1]378        self.onClearSlicer(event)
[ef0c170]379        wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
[1bf33c1]380       
[7ab9241]381    def onBoxSum(self,event):
382        from boxSum import BoxSum
383        self.onClearSlicer(event)
[54cc36a]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
[8a7a21b]404       
405        from slicerpanel import SlicerPanel
[54cc36a]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"
[8a7a21b]417       
418    def onBoxavgX(self,event):
419        from boxSlicer import BoxInteractorX
[38224f10]420        self.onClearSlicer(event)
[8a7a21b]421        wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
[aa1b747]422       
423        from slicerpanel import SlicerPanel
424        new_panel = SlicerPanel(self.parent, -1, style=wx.RAISED_BORDER)
425       
[54cc36a]426        from sans.guicomm.events import SlicerPanelEvent
427        wx.PostEvent(self.parent, SlicerPanelEvent (panel= new_panel))
[aa1b747]428       
[8a7a21b]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)
[b319def8]436       
[8a7a21b]437        from sans.guicomm.events import SlicerParameterEvent
438        wx.PostEvent(self.parent, SlicerParameterEvent (panel= new_panel))
439         
[1bf33c1]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       
[0f6d05f8]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.