source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ ed2ea6a

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 ed2ea6a was 6d920cd, checked in by Gervaise Alina <gervyh@…>, 16 years ago

print statement removed

  • Property mode set to 100644
File size: 19.5 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, math
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             
184        id = wx.NewId()
185        slicerpop.Append(id, '&Save image')
186        wx.EVT_MENU(self, id, self.onSaveImage)
187       
188        id = wx.NewId()
189        slicerpop.Append(id,'&Print image', 'Print image ')
190        wx.EVT_MENU(self, id, self.onPrint)
191       
192        id = wx.NewId()
193        slicerpop.Append(id,'&Print Preview', 'image preview for print')
194        wx.EVT_MENU(self, id, self.onPrinterPreview)
195       
196        slicerpop.AppendSeparator()
197        item_list = self.parent.get_context_menu(self.graph)
198        if (not item_list==None) and (not len(item_list)==0):
199               
200                for item in item_list:
201                    try:
202                        id = wx.NewId()
203                        slicerpop.Append(id, item[0], item[1])
204                        wx.EVT_MENU(self, id, item[2])
205                    except:
206                        pass
207                        #print sys.exc_value
208                        #print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
209       
210                slicerpop.AppendSeparator()
211       
212        id = wx.NewId()
213        slicerpop.Append(id, '&Edit Detector Parameters')
214        wx.EVT_MENU(self, id, self._onEditDetector) 
215       
216        id = wx.NewId()
217        slicerpop.Append(id, '&Perform circular average')
218        wx.EVT_MENU(self, id, self.onCircular) 
219       
220        id = wx.NewId()
221        slicerpop.Append(id, '&Sector [Q view]')
222        wx.EVT_MENU(self, id, self.onSectorQ) 
223       
224        id = wx.NewId()
225        slicerpop.Append(id, '&Annulus [Phi view ]')
226        wx.EVT_MENU(self, id, self.onSectorPhi) 
227       
228        id = wx.NewId()
229        slicerpop.Append(id, '&Box Sum')
230        wx.EVT_MENU(self, id, self.onBoxSum) 
231       
232        id = wx.NewId()
233        slicerpop.Append(id, '&Box averaging in Qx')
234        wx.EVT_MENU(self, id, self.onBoxavgX) 
235       
236        id = wx.NewId()
237        slicerpop.Append(id, '&Box averaging in Qy')
238        wx.EVT_MENU(self, id, self.onBoxavgY) 
239        if self.slicer !=None:
240            id = wx.NewId()
241            slicerpop.Append(id, '&Clear slicer')
242            wx.EVT_MENU(self, id,  self.onClearSlicer) 
243       
244            id = wx.NewId()
245            slicerpop.Append(id, '&Edit Slicer Parameters')
246            wx.EVT_MENU(self, id, self._onEditSlicer) 
247        slicerpop.AppendSeparator() 
248       
249     
250        id = wx.NewId()
251        slicerpop.Append(id, '&Toggle Linear/Log scale')
252        wx.EVT_MENU(self, id, self._onToggleScale) 
253                 
254     
255        pos = event.GetPosition()
256        pos = self.ScreenToClient(pos)
257        self.PopupMenu(slicerpop, pos)
258   
259    def _onEditDetector(self, event):
260        """
261        """
262        #print "edit detortor param",self.zmin_2D, self.zmax_2D
263        import detector_dialog
264        dialog = detector_dialog.DetectorDialog(self, -1,base=self.parent)
265        xnpts = len(self.data2D.x_bins)
266        ynpts = len(self.data2D.y_bins)
267        xmax = max(self.data2D.xmin, self.data2D.xmax)
268        ymax = max(self.data2D.ymin, self.data2D.ymax)
269        qmax = math.sqrt(math.pow(xmax,2)+math.pow(ymax,2))
270        beam = self.data2D.xmin
271        zmin = self.zmin_2D
272        zmax = self.zmax_2D
273        dialog.setContent(xnpts=xnpts,ynpts=ynpts,qmax=qmax,
274                           beam=self.data2D.xmin,
275                           zmin = self.zmin_2D,
276                          zmax = self.zmax_2D)
277        if dialog.ShowModal() == wx.ID_OK:
278            evt = dialog.getContent()
279            self.zmin_2D = evt.zmin
280            self.zmax_2D = evt.zmax
281       
282        dialog.Destroy()
283        #print "zmn ,zmax", self.zmin_2D, self.zmax_2D
284        self.image(data= self.data2D.data,
285                   xmin= self.data2D.xmin,
286                   xmax= self.data2D.xmax,
287                   ymin= self.data2D.ymin,
288                   ymax= self.data2D.ymax,
289                   zmin= self.zmin_2D,
290                   zmax= self.zmax_2D,
291                   color=0,symbol=0,label='data2D')
292        #self.graph.render(self)
293        self.subplot.figure.canvas.draw_idle()
294       
295    def get_corrected_data(self):
296        # Protect against empty data set
297        if self.data == None:
298            return None
299        import copy
300        output = copy.deepcopy(self.data)
301        return output
302    def freeze_axes(self):
303        self.axes_frozen = True
304       
305    def thaw_axes(self):
306        self.axes_frozen = False
307       
308    def onMouseMotion(self,event):
309        pass
310    def onWheel(self, event):
311        pass   
312    def update(self, draw=True):
313        """
314            Respond to changes in the model by recalculating the
315            profiles and resetting the widgets.
316        """
317        #self.slicer.update()
318        self.draw()
319       
320       
321    def _getEmptySlicerEvent(self):
322        return SlicerEvent(type=None,
323                           params=None,
324                           obj_class=None)
325    def _onEVT_INTERNAL(self, event):
326        """
327            I don't understand why Unbind followed by a Bind
328            using a modified self.slicer doesn't work.
329            For now, I post a clear event followed by
330            a new slicer event...
331        """
332        self._setSlicer(event.slicer)
333           
334    def _setSlicer(self, slicer):
335        # Clear current slicer
336        #printEVT("Plotter2D._setSlicer %s" % slicer)
337       
338        if not self.slicer == None: 
339            self.slicer.clear()           
340           
341        self.slicer_z += 1
342        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
343        #print "come here"
344        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
345        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
346       
347        self.update()
348        self.slicer.update()
349       
350        # Post slicer event
351        event = self._getEmptySlicerEvent()
352        event.type = self.slicer.__class__.__name__
353       
354        event.obj_class = self.slicer.__class__
355        event.params = self.slicer.get_params()
356        #print "Plotter2D: event.type",event.type,event.params, self.parent
357       
358        #wx.PostEvent(self.parent, event)
359        wx.PostEvent(self, event)
360
361    def onCircular(self, event):
362        """
363            perform circular averaging on Data2D
364        """
365       
366        from DataLoader.manipulations import CircularAverage
367        import math
368        self.qmax= max(math.fabs(self.data2D.xmax),math.fabs(self.data2D.xmin))
369        self.ymax=max(math.fabs(self.data2D.ymax),math.fabs(self.data2D.ymin))
370        self.radius= math.sqrt( math.pow(self.qmax,2)+math.pow(self.ymax,2)) 
371        #print "radius?",self.radius
372        # bin_width = self.qmax -self.qmin/nbins
373        #nbins= 30
374        bin_width = (self.qmax +self.qmax)/100
375       
376        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
377       
378        circ = Circle(self.data2D)
379        from sans.guiframe.dataFitting import Data1D
380        if hasattr(circ,"dxl"):
381            dxl= circ.dxl
382        else:
383            dxl= None
384        if hasattr(circ,"dxw"):
385            dxw= circ.dxw
386        else:
387            dxw= None
388       
389        new_plot = Data1D(x=circ.x,y=circ.y,dy=circ.dy,dxl=dxl,dxw=dxw)
390        new_plot.name = "Circ avg "+ self.data2D.name
391        new_plot.source=self.data2D.source
392        #new_plot.info=self.data2D.info
393        new_plot.interactive = True
394        #print "loader output.detector",output.source
395        new_plot.detector =self.data2D.detector
396       
397        # If the data file does not tell us what the axes are, just assume...
398        new_plot.xaxis("\\rm{Q}","A^{-1}")
399        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
400        new_plot.group_id = "Circ avg "+ self.data2D.name
401        new_plot.id = "Circ avg "+ self.data2D.name
402        self.scale = 'log'
403       
404        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
405       
406    def _onEditSlicer(self, event):
407        if self.slicer !=None:
408            from SlicerParameters import SlicerParameterPanel
409            dialog = SlicerParameterPanel(self, -1, "Slicer Parameters")
410            #dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
411            dialog.set_slicer(self.slicer.__class__.__name__,
412                            self.slicer.get_params())
413            if dialog.ShowModal() == wx.ID_OK:
414                dialog.Destroy() 
415       
416    def onSectorQ(self, event):
417        """
418            Perform sector averaging on Q
419        """
420        #print "onsector self.data2Dxmax",self.data2D.xmax, self.parent
421        from SectorSlicer import SectorInteractor
422        self.onClearSlicer(event)
423        #wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
424        wx.PostEvent(self, InternalEvent(slicer= SectorInteractor))
425       
426    def onSectorPhi(self, event):
427        """
428            Perform sector averaging on Phi
429        """
430        from AnnulusSlicer import AnnulusInteractor
431        self.onClearSlicer(event)
432        #wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
433        wx.PostEvent(self, InternalEvent(slicer= AnnulusInteractor))
434       
435    def onBoxSum(self,event):
436        from boxSum import BoxSum
437        self.onClearSlicer(event)
438        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxSum))
439        if not self.slicer == None: 
440            self.slicer.clear()             
441        self.slicer_z += 1
442        self.slicer =  BoxSum(self, self.subplot, zorder=self.slicer_z)
443        #print "come here"
444        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
445        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
446       
447        self.update()
448        self.slicer.update()
449       
450        # Post slicer event
451        event = self._getEmptySlicerEvent()
452        event.type = self.slicer.__class__.__name__
453       
454       
455        event.obj_class = self.slicer.__class__
456        event.params = self.slicer.get_params()
457        #print "Plotter2D: event.type",event.type,event.params, self.parent
458       
459        from slicerpanel import SlicerPanel
460        new_panel = SlicerPanel(parent= self.parent,id= -1,base= self,type=event.type,
461                                 params=event.params, style=wx.RAISED_BORDER)
462        #new_panel.set_slicer(self.slicer.__class__.__name__,
463        new_panel.window_caption=str(self.slicer_z)+self.slicer.__class__.__name__+" "+ str(self.data2D.name)
464        new_panel.window_name = str(self.slicer_z)+self.slicer.__class__.__name__+" "+ str(self.data2D.name)
465        self.panel_slicer= new_panel
466        self.slicer.set_panel_name( name= new_panel.window_caption)
467        #wx.PostEvent(self.panel_slicer, event)
468        from sans.guicomm.events import SlicerPanelEvent
469        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
470        #print "finish box sum"
471       
472    def onBoxavgX(self,event):
473        from boxSlicer import BoxInteractorX
474        self.onClearSlicer(event)
475        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
476        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorX))
477       
478       
479    def onBoxavgY(self,event):
480        from boxSlicer import BoxInteractorY
481        self.onClearSlicer(event)
482        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorY))
483        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
484       
485    def onClearSlicer(self, event):
486        """
487            Clear the slicer on the plot
488        """
489        if not self.slicer==None:
490            self.slicer.clear()
491            self.subplot.figure.canvas.draw()
492            self.slicer = None
493       
494            # Post slicer None event
495            event = self._getEmptySlicerEvent()
496            #wx.PostEvent(self.parent, event)
497            wx.PostEvent(self, event)
498         
499 
500       
501    def _onToggleScale(self, event):
502        """
503            toggle pixel scale and replot image
504        """
505        if self.scale == 'log':
506            self.scale = 'linear'
507        else:
508            self.scale = 'log'
509        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
510                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
511        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
512       
513        """     
514            #TODO: this name should be changed to something more appropriate
515            # Don't forget that changing this name will mean changing code
516            # in plotting.py
517             
518            # Update the plottable with the new data
519           
520            #TODO: we should have a method to do this,
521            #      something along the lines of:
522            #      plottable1.update_data_from_plottable(plottable2)
523           
524            self.plots[event.plot.name].xmin = event.plot.xmin
525            self.plots[event.plot.name].xmax = event.plot.xmax
526            self.plots[event.plot.name].ymin = event.plot.ymin
527            self.plots[event.plot.name].ymax = event.plot.ymax
528            self.plots[event.plot.name].data = event.plot.data
529            self.plots[event.plot.name].err_data = event.plot.err_data
530        """
Note: See TracBrowser for help on using the repository browser.