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

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

change edit slicer : change the dialog parent

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