source: sasview/guiframe/local_perspectives/plotting/Plotter2D.py @ 8dddbd4

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 8dddbd4 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
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
[ea290ee]13import sys, math
[0d9dae8]14import pylab
15
[1bf33c1]16import danse.common.plottools
17from danse.common.plottools.PlotPanel import PlotPanel
18from danse.common.plottools.plottables import Graph,Data1D
[0bd2cd8]19from sans.guicomm.events import EVT_NEW_PLOT
[0d9dae8]20from sans.guicomm.events import EVT_SLICER_PARS
21from sans.guicomm.events import StatusEvent ,NewPlotEvent,SlicerEvent
22from sans.guiframe.utils import PanelMenu
[1bf33c1]23from binder import BindArtist
24from Plotter1D import ModelPanel1D
[0d9dae8]25(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
26
[1bf33c1]27
[0d9dae8]28
29DEFAULT_QMAX = 0.05
[1bf33c1]30DEFAULT_QSTEP = 0.001
31DEFAULT_BEAM = 0.005
[ef0c170]32BIN_WIDTH = 1.0
[0d9dae8]33
34
35
36
[1bf33c1]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
[ac9a5f6]65        self.data =data2d.data
[1bf33c1]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
[54cc36a]75       
[f15ed33]76        self.slicer_z = 5
[1bf33c1]77        self.slicer = None
[d468daa]78        #self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
79        self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
[1bf33c1]80        self.axes_frozen = False
81       
[54cc36a]82        self.panel_slicer=None
83        #self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
[1bf33c1]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)
[54cc36a]89        #self.Bind(boxSum.EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PARS)
[0bd125d]90        #self.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
[0bd2cd8]91       
[0d9dae8]92       
93       
[54cc36a]94    def _onEVT_SLICER_PARS(self, event):
[0bd125d]95        print "paramaters entered on slicer panel", event.type, event.params
[0d9dae8]96        self.slicer.set_params(event.params)
[0bd125d]97        #from sans.guicomm.events import SlicerPanelEvent
98        #wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
[0d9dae8]99       
[1bf33c1]100    def _onEVT_1DREPLOT(self, event):
101        """
102            Data is ready to be displayed
[4b91fd1]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             
[1bf33c1]108            @param event: data event
109        """
[50cbace]110             
111        self.data2D= event.plot
112        self.data =event.plot.data
[1bf33c1]113        #TODO: Check for existence of plot attribute
[ab8f936]114
[1bf33c1]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
[ab8f936]120       
[1bf33c1]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__:
[ab8f936]127                #overwrite a plottable using the same name
[1bf33c1]128                self.graph.delete(self.plots[event.plot.name])
129            else:
[ab8f936]130                # plottable is already draw on the panel
[1bf33c1]131                is_new = False
[ab8f936]132           
133        if is_new:
134            # a new plottable overwrites a plotted one  using the same id
135            for plottable in self.plots.itervalues():
[e48a62e]136                if hasattr(event.plot,"id"):
137                    if event.plot.id==plottable.id :
138                        self.graph.delete(plottable)
[ab8f936]139           
140            self.plots[event.plot.name] = event.plot
141            self.graph.add(self.plots[event.plot.name])
142        else:
[4b91fd1]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
[ac9a5f6]155            # update qmax with the new xmax of data plotted
156            self.qmax= event.plot.xmax
[4b91fd1]157           
[ac9a5f6]158        self.slicer= None
[ffd23b5]159       
[1bf33c1]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)
[ab8f936]164           
[1bf33c1]165        #if event.plot._yunit != self.graph.prop["yunit"]:
166        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
[0690e1d]167        self.graph.title(self.data2D.name)
[1bf33c1]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)
[9a585d0]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       
[1ce365f8]192        id = wx.NewId()
[18eba35]193        slicerpop.Append(id,'&Print Preview', 'image preview for print')
[1ce365f8]194        wx.EVT_MENU(self, id, self.onPrinterPreview)
195       
[9a585d0]196        slicerpop.AppendSeparator()
[1bf33c1]197        item_list = self.parent.get_context_menu(self.graph)
198        if (not item_list==None) and (not len(item_list)==0):
[d468daa]199               
[1bf33c1]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:
[d468daa]206                        pass
207                        #print sys.exc_value
208                        #print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
[1bf33c1]209       
[9a585d0]210                slicerpop.AppendSeparator()
[8bd764d]211       
[ea290ee]212        id = wx.NewId()
213        slicerpop.Append(id, '&Edit Detector Parameters')
214        wx.EVT_MENU(self, id, self._onEditDetector) 
[1bf33c1]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()
[ef0c170]221        slicerpop.Append(id, '&Sector [Q view]')
[1bf33c1]222        wx.EVT_MENU(self, id, self.onSectorQ) 
223       
224        id = wx.NewId()
[ef0c170]225        slicerpop.Append(id, '&Annulus [Phi view ]')
[1bf33c1]226        wx.EVT_MENU(self, id, self.onSectorPhi) 
[92c2345]227       
[7ab9241]228        id = wx.NewId()
229        slicerpop.Append(id, '&Box Sum')
230        wx.EVT_MENU(self, id, self.onBoxSum) 
231       
[38224f10]232        id = wx.NewId()
[8a7a21b]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) 
[92c2345]239        if self.slicer !=None:
240            id = wx.NewId()
241            slicerpop.Append(id, '&Clear slicer')
242            wx.EVT_MENU(self, id,  self.onClearSlicer) 
[1bf33c1]243       
[92c2345]244            id = wx.NewId()
245            slicerpop.Append(id, '&Edit Slicer Parameters')
246            wx.EVT_MENU(self, id, self._onEditSlicer) 
[d468daa]247        slicerpop.AppendSeparator() 
[9a585d0]248       
249        """
[d468daa]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       
[1ce365f8]255       
[9a585d0]256        """
[1bf33c1]257        id = wx.NewId()
258        slicerpop.Append(id, '&Toggle Linear/Log scale')
259        wx.EVT_MENU(self, id, self._onToggleScale) 
[d468daa]260                 
261     
[1bf33c1]262        pos = event.GetPosition()
263        pos = self.ScreenToClient(pos)
264        self.PopupMenu(slicerpop, pos)
[8bd764d]265   
[ea290ee]266    def _onEditDetector(self, event):
267        print "edit detortor param",self.zmin_2D, self.zmax_2D
268        import detector_dialog
[9a585d0]269        dialog = detector_dialog.DetectorDialog(self, -1,base=self.parent)
[ea290ee]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()
[1bf33c1]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)
[d468daa]348        #print "come here"
[240c805]349        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
350        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
351       
[1bf33c1]352        self.update()
353        self.slicer.update()
354       
355        # Post slicer event
356        event = self._getEmptySlicerEvent()
357        event.type = self.slicer.__class__.__name__
[54cc36a]358       
[1bf33c1]359        event.obj_class = self.slicer.__class__
360        event.params = self.slicer.get_params()
[d468daa]361        #print "Plotter2D: event.type",event.type,event.params, self.parent
[54cc36a]362       
[d468daa]363        #wx.PostEvent(self.parent, event)
364        wx.PostEvent(self, event)
[1bf33c1]365
366    def onCircular(self, event):
367        """
368            perform circular averaging on Data2D
369        """
370       
371        from DataLoader.manipulations import CircularAverage
[ef0c170]372        import math
[216efab]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)) 
[d468daa]376        #print "radius?",self.radius
[c73d871]377        # bin_width = self.qmax -self.qmin/nbins
378        #nbins= 30
[8f584c9]379        bin_width = (self.qmax +self.qmax)/100
[c73d871]380       
381        Circle = CircularAverage( r_min=0, r_max=self.radius, bin_width=bin_width)
382       
[1bf33c1]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
[ef0c170]393       
[1bf33c1]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
[50cbace]397        #new_plot.info=self.data2D.info
[1bf33c1]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...
[8f584c9]403        new_plot.xaxis("\\rm{Q}","A^{-1}")
[1bf33c1]404        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
405        new_plot.group_id = "Circ avg "+ self.data2D.name
[8b30e62]406        new_plot.id = "Circ avg "+ self.data2D.name
[1bf33c1]407        self.scale = 'log'
[ef0c170]408       
[1bf33c1]409        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name))
[ef0c170]410       
[1bf33c1]411    def _onEditSlicer(self, event):
412        if self.slicer !=None:
413            from SlicerParameters import SlicerParameterPanel
[4f8a00c]414            dialog = SlicerParameterPanel(self, -1, "Slicer Parameters")
415            #dialog = SlicerParameterPanel(self.parent, -1, "Slicer Parameters")
[1bf33c1]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        """
[d468daa]425        #print "onsector self.data2Dxmax",self.data2D.xmax, self.parent
[ef0c170]426        from SectorSlicer import SectorInteractor
[1bf33c1]427        self.onClearSlicer(event)
[d468daa]428        #wx.PostEvent(self.parent, InternalEvent(slicer= SectorInteractor))
429        wx.PostEvent(self, InternalEvent(slicer= SectorInteractor))
[1bf33c1]430       
431    def onSectorPhi(self, event):
432        """
433            Perform sector averaging on Phi
434        """
[ef0c170]435        from AnnulusSlicer import AnnulusInteractor
[1bf33c1]436        self.onClearSlicer(event)
[d468daa]437        #wx.PostEvent(self.parent, InternalEvent(slicer= AnnulusInteractor))
438        wx.PostEvent(self, InternalEvent(slicer= AnnulusInteractor))
[1bf33c1]439       
[7ab9241]440    def onBoxSum(self,event):
441        from boxSum import BoxSum
442        self.onClearSlicer(event)
[54cc36a]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)
[d468daa]448        #print "come here"
[54cc36a]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()
[d468daa]462        #print "Plotter2D: event.type",event.type,event.params, self.parent
[8a7a21b]463       
464        from slicerpanel import SlicerPanel
[0d9dae8]465        new_panel = SlicerPanel(parent= self.parent,id= -1,base= self,type=event.type,
[54cc36a]466                                 params=event.params, style=wx.RAISED_BORDER)
467        #new_panel.set_slicer(self.slicer.__class__.__name__,
[0bd125d]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)
[54cc36a]470        self.panel_slicer= new_panel
[0bd2cd8]471        self.slicer.set_panel_name( name= new_panel.window_caption)
472        #wx.PostEvent(self.panel_slicer, event)
[54cc36a]473        from sans.guicomm.events import SlicerPanelEvent
474        wx.PostEvent(self.parent, SlicerPanelEvent (panel= self.panel_slicer))
[d468daa]475        #print "finish box sum"
[8a7a21b]476       
477    def onBoxavgX(self,event):
478        from boxSlicer import BoxInteractorX
[38224f10]479        self.onClearSlicer(event)
[d468daa]480        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorX))
481        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorX))
482       
483       
[8a7a21b]484    def onBoxavgY(self,event):
485        from boxSlicer import BoxInteractorY
486        self.onClearSlicer(event)
[d468daa]487        wx.PostEvent(self, InternalEvent(slicer= BoxInteractorY))
488        #wx.PostEvent(self.parent, InternalEvent(slicer= BoxInteractorY))
[b319def8]489       
[1bf33c1]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()
[d468daa]501            #wx.PostEvent(self.parent, event)
502            wx.PostEvent(self, event)
[1bf33c1]503         
[ea290ee]504 
[1bf33c1]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       
[0f6d05f8]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.