source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 7292e8a

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

context menu improve for xml

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