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

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

edit color for detector

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