source: sasview/guiframe/local_perspectives/plotting/DataPanel.py @ 917850d

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

sector averaging

  • Property mode set to 100644
File size: 21.5 KB
RevLine 
[743d75c]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
14import danse.common.plottools
15from danse.common.plottools.PlotPanel import PlotPanel
16from danse.common.plottools.plottables import Graph,Data1D
17from sans.guicomm.events import EVT_NEW_PLOT
[ce64735]18from sans.guicomm.events import StatusEvent ,NewPlotEvent
[743d75c]19
[b06ef8c]20from binder import BindArtist
21#from SlicerParameters import SlicerEvent
22#(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
23DEFAULT_QMAX = 0.05
24
25DEFAULT_QSTEP = 0.001
26DEFAULT_BEAM = 0.005
27
28import pylab
29
[743d75c]30class PanelMenu(wx.Menu):
31    plots = None
32    graph = None
33   
34    def set_plots(self, plots):
35        self.plots = plots
36   
37    def set_graph(self, graph):
38        self.graph = graph
39class View1DPanel1D(PlotPanel):
40    """
41        Plot panel for use with the GUI manager
42    """
43   
44    ## Internal name for the AUI manager
45    window_name = "plotpanel"
46    ## Title to appear on top of the window
47    window_caption = "Plot Panel"
48    ## Flag to tell the GUI manager that this panel is not
49    #  tied to any perspective
50    ALWAYS_ON = True
51    ## Group ID
52    group_id = None
53   
54    def __init__(self, parent, id = -1, color = None,\
55        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
56        """
57            Initialize the panel
58        """
59        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
60       
61        ## Reference to the parent window
62        self.parent = parent
63        ## Plottables
64        self.plots = {}
65       
66        ## Unique ID (from gui_manager)
67        self.uid = None
68       
69        ## Action IDs for internal call-backs
70        self.action_ids = {}
71       
72        ## Graph       
73        self.graph = Graph()
74        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
75        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
76        self.graph.render(self)
77   
78    def _reset(self):
79        """
80            Resets internal data and graph
81        """   
82        self.graph.reset()
83        self.plots      = {}
84        self.action_ids = {}
85   
86    def _onEVT_1DREPLOT(self, event):
87        """
88            Data is ready to be displayed
89            @param event: data event
90        """
91        #TODO: Check for existence of plot attribute
92
93        # Check whether this is a replot. If we ask for a replot
94        # and the plottable no longer exists, ignore the event.
95        if hasattr(event, "update") and event.update==True \
96            and event.plot.name not in self.plots.keys():
97            return
98       
99        if hasattr(event, "reset"):
100            self._reset()
101       
102        is_new = True
[35d1092]103        print "model panel name",event.plot.name
[743d75c]104        if event.plot.name in self.plots.keys():
105            # Check whether the class of plottable changed
[35d1092]106            print "model panel",event.plot.name,event.plot.__class__
[743d75c]107            if not event.plot.__class__==self.plots[event.plot.name].__class__:
108                self.graph.delete(self.plots[event.plot.name])
109            else:
110                is_new = False
111       
112        if is_new:
113            self.plots[event.plot.name] = event.plot
114            self.graph.add(self.plots[event.plot.name])
115        else:
116            self.plots[event.plot.name].x = event.plot.x   
117            self.plots[event.plot.name].y = event.plot.y   
118            self.plots[event.plot.name].dy = event.plot.dy 
119            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
120                self.plots[event.plot.name].dx = event.plot.dx   
121 
122       
123        # Check axis labels
124        #TODO: Should re-factor this
125        #if event.plot._xunit != self.graph.prop["xunit"]:
126        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
127           
128        #if event.plot._yunit != self.graph.prop["yunit"]:
129        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
130     
131        # Set the view scale for all plots
132        self._onEVT_FUNC_PROPERTY()
133     
134        self.graph.render(self)
135        self.subplot.figure.canvas.draw_idle()
136
137    def onLeftDown(self,event): 
138        """ left button down and ready to drag"""
139           
140        PlotPanel.onLeftDown(self, event)
141        ax = event.inaxes
142        if ax != None:
143            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
144            wx.PostEvent(self.parent, StatusEvent(status=position))
145
146    def _onRemove(self, event):
147        """
148        """
149        if not self.graph.selected_plottable == None:
150            print self.graph.selected_plottable
151           
152           
153            self.graph.delete(self.plots[self.graph.selected_plottable])
154            del self.plots[self.graph.selected_plottable]
155            self.graph.render(self)
156            self.subplot.figure.canvas.draw_idle()   
157           
158
159    def onContextMenu(self, event):
160        """
161            1D plot context menu
162            @param event: wx context event
163        """
164        #slicerpop = wx.Menu()
165        slicerpop = PanelMenu()
166        slicerpop.set_plots(self.plots)
167        slicerpop.set_graph(self.graph)
168               
169        # Option to save the data displayed
170       
171        #for plot in self.graph.plottables:
172        if self.graph.selected_plottable in self.plots:
173            plot = self.plots[self.graph.selected_plottable]
174            id = wx.NewId()
175            name = plot.name
176            slicerpop.Append(id, "&Save %s points" % name)
177            self.action_ids[str(id)] = plot
178            wx.EVT_MENU(self, id, self._onSave)
179               
180            # Option to delete plottable
181            id = wx.NewId()
182            slicerpop.Append(id, "Remove %s curve" % name)
183            self.action_ids[str(id)] = plot
184            wx.EVT_MENU(self, id, self._onRemove)
185           
186            # Option to hide
187            #TODO: implement functionality to hide a plottable (legend click)
188            slicerpop.AppendSeparator()
189               
190        # Various plot options
191        id = wx.NewId()
192        slicerpop.Append(id,'&Save image', 'Save image as PNG')
193        wx.EVT_MENU(self, id, self.onSaveImage)
194       
195       
196        item_list = self.parent.get_context_menu(self.graph)
197        if (not item_list==None) and (not len(item_list)==0):
198                slicerpop.AppendSeparator()
199                for item in item_list:
200                    try:
201                        id = wx.NewId()
202                        slicerpop.Append(id, item[0], item[1])
203                        wx.EVT_MENU(self, id, item[2])
204                    except:
205                        print sys.exc_value
206                        print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
207       
208        slicerpop.AppendSeparator()
209       
210        if self.graph.selected_plottable in self.plots:
211            if self.plots[self.graph.selected_plottable].__class__.__name__=="Theory1D":
212                id = wx.NewId()
213                slicerpop.Append(id, '&Add errors to data')
214                wx.EVT_MENU(self, id, self._on_add_errors)
215            else:
216                id = wx.NewId()
217                slicerpop.Append(id, '&Linear fit')
218                wx.EVT_MENU(self, id, self.onFitting)
219               
220       
221
222        id = wx.NewId()
223        slicerpop.Append(id, '&Change scale')
224        wx.EVT_MENU(self, id, self._onProperties)
225       
226        id = wx.NewId()
227        #slicerpop.AppendSeparator()
228        slicerpop.Append(id, '&Reset Graph')
229        wx.EVT_MENU(self, id, self.onResetGraph)       
230
231        pos = event.GetPosition()
232        pos = self.ScreenToClient(pos)
233        self.PopupMenu(slicerpop, pos)
234   
235   
236    def _on_add_errors(self, evt):
237        """
238            Compute reasonable errors for a data set without
239            errors and transorm the plottable to a Data1D
240        """
241        import math
242        import numpy
243        import time
244       
245        if not self.graph.selected_plottable == None:
246            length = len(self.plots[self.graph.selected_plottable].x)
247            dy = numpy.zeros(length)
248            for i in range(length):
249                dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])
250               
251            new_plot = Data1D(self.plots[self.graph.selected_plottable].x,
252                              self.plots[self.graph.selected_plottable].y,
253                              dy=dy)
254            new_plot.interactive = True
255            new_plot.name = self.plots[self.graph.selected_plottable].name
256            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
257                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
258            else:
259                new_plot.group_id = str(time.time())
260           
261            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
262            new_plot.xaxis(label, unit)
263            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
264            new_plot.yaxis(label, unit)
265           
266            self.graph.delete(self.plots[self.graph.selected_plottable])
267           
268            self.graph.add(new_plot)
269            self.plots[self.graph.selected_plottable]=new_plot
270           
271            self.graph.render(self)
272            self.subplot.figure.canvas.draw_idle()   
273   
274    def _onSave(self, evt):
275        """
276            Save a data set to a text file
277            @param evt: Menu event
278        """
279        import os
280        id = str(evt.GetId())
281        if id in self.action_ids:         
282           
283            path = None
284            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
285            if dlg.ShowModal() == wx.ID_OK:
286                path = dlg.GetPath()
287                mypath = os.path.basename(path)
288                print path
289            dlg.Destroy()
290           
291            if not path == None:
292                out = open(path, 'w')
293                has_errors = True
294                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
295                    has_errors = False
296                   
297                # Sanity check
298                if has_errors:
299                    try:
300                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
301                            print "Y and dY have different lengths"
302                            has_errors = False
303                    except:
304                        has_errors = False
305               
306                if has_errors:
307                    out.write("<X>   <Y>   <dY>\n")
308                else:
309                    out.write("<X>   <Y>\n")
310                   
311                for i in range(len(self.action_ids[id].x)):
312                    if has_errors:
313                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
314                                                    self.action_ids[id].y[i],
315                                                    self.action_ids[id].dy[i]))
316                    else:
317                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
318                                                self.action_ids[id].y[i]))
319                       
320                out.close()
321   
322   
323    def _onToggleScale(self, event):
324        if self.get_yscale() == 'log':
325            self.set_yscale('linear')
326        else:
327            self.set_yscale('log')
328        self.subplot.figure.canvas.draw_idle()   
329       
330class View1DPanel2D( View1DPanel1D):
331    """
332        Plot panel for use with the GUI manager
333    """
334   
335    ## Internal name for the AUI manager
336    window_name = "plotpanel"
337    ## Title to appear on top of the window
338    window_caption = "Plot Panel"
339    ## Flag to tell the GUI manager that this panel is not
340    #  tied to any perspective
341    ALWAYS_ON = True
342    ## Group ID
343    group_id = None
344   
345    def __init__(self, parent, id = -1,data2d=None, color = None,\
346        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
347        """
348            Initialize the panel
349        """
350        View1DPanel1D.__init__(self, parent, id = id, style = style, **kwargs)
351       
352        ## Reference to the parent window
353        self.parent = parent
354        ## Plottables
355        self.plots = {}
[b06ef8c]356        self.data2D= data2d
357        self.data = data2d.data
[743d75c]358        ## Unique ID (from gui_manager)
359        self.uid = None
360       
361        ## Action IDs for internal call-backs
362        self.action_ids = {}
[b06ef8c]363        self.connect = BindArtist(self.subplot.figure)
[743d75c]364       
[b06ef8c]365        # Beam stop
366        self.beamstop_radius = DEFAULT_BEAM
367        # Slicer
368       
369        self.qmax = data2d.xmax
370        self.imax= data2d.ymax
371        self.qstep = DEFAULT_QSTEP
372        self.x = pylab.arange(-self.qmax, self.qmax+self.qstep*0.01, self.qstep)
373        self.y = pylab.arange(-self.imax, self.imax+self.qstep*0.01, self.qstep)
374
375        self.slicer_z = 5
376        self.slicer = None
377        #self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
378        self.axes_frozen = False
[743d75c]379        ## Graph       
380        self.graph = Graph()
381        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
382        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
383        self.graph.render(self)
384 
385    def _onEVT_1DREPLOT(self, event):
386        """
387            Data is ready to be displayed
388            @param event: data event
389        """
390        #TODO: Check for existence of plot attribute
391        # Check whether this is a replot. If we ask for a replot
392        # and the plottable no longer exists, ignore the event.
393        if hasattr(event, "update") and event.update==True \
394            and event.plot.name not in self.plots.keys():
395            return
396        if hasattr(event, "reset"):
397            self._reset()
398        is_new = True
399        if event.plot.name in self.plots.keys():
400            # Check whether the class of plottable changed
401            if not event.plot.__class__==self.plots[event.plot.name].__class__:
402                self.graph.delete(self.plots[event.plot.name])
403            else:
404                is_new = False
405        self.plots[event.plot.name] = event.plot
406        #if is_new:
407        self.graph.add(self.plots[event.plot.name])
408       
409
410        # Check axis labels
411        #TODO: Should re-factor this
412        #if event.plot._xunit != self.graph.prop["xunit"]:
413       
414        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
415        #if event.plot._yunit != self.graph.prop["yunit"]:
416        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
417        self.graph.render(self)
418        self.subplot.figure.canvas.draw_idle()
419
420
421    def onContextMenu(self, event):
422        """
423            2D plot context menu
424            @param event: wx context event
425        """
426       
427        #slicerpop = wx.Menu()
428        slicerpop = PanelMenu()
429        slicerpop.set_plots(self.plots)
430        slicerpop.set_graph(self.graph)
431               
432        # Option to save the data displayed
433   
434        # Various plot options
435        id = wx.NewId()
436        slicerpop.Append(id,'&Save image', 'Save image as PNG')
437        wx.EVT_MENU(self, id, self.onSaveImage)
438       
439       
440        item_list = self.parent.get_context_menu(self.graph)
441        if (not item_list==None) and (not len(item_list)==0):
442                slicerpop.AppendSeparator()
443                for item in item_list:
444                    try:
445                        id = wx.NewId()
446                        slicerpop.Append(id, item[0], item[1])
447                        wx.EVT_MENU(self, id, item[2])
448                    except:
449                        print sys.exc_value
450                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
451       
452        slicerpop.AppendSeparator()
453     
454        id = wx.NewId()
[b06ef8c]455        slicerpop.Append(id, '&Sector')
456        wx.EVT_MENU(self, id, self.onSector) 
457       
458     
459       
460        id = wx.NewId()
461        slicerpop.Append(id, '&Clear slicer')
462        wx.EVT_MENU(self, id,  self.onClearSlicer) 
463       
464        id = wx.NewId()
465        slicerpop.Append(id, '&Edit Parameters')
466        wx.EVT_MENU(self, id, self._onEditDetector) 
467       
468        slicerpop.AppendSeparator()
469       
470        id = wx.NewId()
471        slicerpop.Append(id, '&Save image')
472        wx.EVT_MENU(self, id, self.onSaveImage) 
473     
474        id = wx.NewId()
[743d75c]475        slicerpop.Append(id, '&Toggle Linear/Log scale')
476        wx.EVT_MENU(self, id, self._onToggleScale) 
[b06ef8c]477         
[743d75c]478        pos = event.GetPosition()
479        pos = self.ScreenToClient(pos)
480        self.PopupMenu(slicerpop, pos)
[b06ef8c]481   
482    def _setSlicer(self, slicer):
483        # Clear current slicer
484        #printEVT("Plotter2D._setSlicer %s" % slicer)
485       
486        if not self.slicer == None: 
487            self.slicer.clear()           
488           
489        self.slicer_z += 1
490        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
491        self.subplot.set_ylim(-self.qmax, self.qmax)
492        self.subplot.set_xlim(-self.qmax, self.qmax)
493        self.update()
494        self.slicer.update()
495       
496       
497    def get_corrected_data(self):
498        # Protect against empty data set
499        if self.data == None:
500            return None
501        import copy
502        output = copy.deepcopy(self.data)
503        return output
504    def freeze_axes(self):
505        self.axes_frozen = True
506       
507    def thaw_axes(self):
508        self.axes_frozen = False
509       
510    def onMouseMotion(self,event):
511        pass
512    def onWheel(self, event):
513        pass   
514    def update(self, draw=True):
515        """
516            Respond to changes in the model by recalculating the
517            profiles and resetting the widgets.
518        """
519        #self.slicer.update()
520        self.draw()
521           
522 
523    def onSector(self, event):
524       
525        from SectorSlicer import SectorInteractor
526             
527        self.slicer_z += 1
528        self.slicer = SectorInteractor(self, self.subplot, zorder=self.slicer_z)
529        self.subplot.set_ylim(-self.qmax, self.qmax)
530        self.subplot.set_xlim(-self.qmax, self.qmax)
531        self.update()
532        self.slicer.update()
533       
534       
535    def onClearSlicer(self, event):
536        print "on clear" 
537       
538         
539    def _onEditDetector(self, event):
540        print "on parameter"
541       
542       
[743d75c]543    def _onToggleScale(self, event):
544        """
545            toggle axis and replot image
546        """
547        if self.scale == 'log':
548            self.scale = 'linear'
549        else:
550            self.scale = 'log'
551        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
552                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
553        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
554       
555class View1DModelPanel2D( View1DPanel2D):
556    """
557        Plot panel for use with the GUI manager
558    """
559   
560    ## Internal name for the AUI manager
561    window_name = "plotpanel"
562    ## Title to appear on top of the window
563    window_caption = "Plot Panel"
564    ## Flag to tell the GUI manager that this panel is not
565    #  tied to any perspective
566    ALWAYS_ON = True
567    ## Group ID
568    group_id = None
569   
570    def __init__(self, parent, id = -1,color = None,\
571        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
572        """
573            Initialize the panel
574        """
575        View1DPanel2D.__init__(self, parent, id = id, style = style, **kwargs)
576        ## Reference to the parent window
577        self.parent = parent
578        ## Plottables
579        self.plots = {}
580        print "Model 2d panel"
581       
582        ## Unique ID (from gui_manager)
583        self.uid = None
584       
585        ## Action IDs for internal call-backs
586        self.action_ids = {}
587       
588        ## Graph       
589        self.graph = Graph()
590        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
591        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
592        self.graph.render(self)
593    def onContextMenu(self, event):
594        # Slicer plot popup menu
595         #slicerpop = wx.Menu()
596        slicerpop = PanelMenu()
597        slicerpop.set_plots(self.plots)
598        slicerpop.set_graph(self.graph)
599               
600        # Option to save the data displayed
601   
602        # Various plot options
603        id = wx.NewId()
604        slicerpop.Append(id,'&Save image', 'Save image as PNG')
605        wx.EVT_MENU(self, id, self.onSaveImage)
606       
607       
608        item_list = self.parent.get_context_menu(self.graph)
609        if (not item_list==None) and (not len(item_list)==0):
610                slicerpop.AppendSeparator()
611                for item in item_list:
612                    try:
613                        id = wx.NewId()
614                        slicerpop.Append(id, item[0], item[1])
615                        wx.EVT_MENU(self, id, item[2])
616                    except:
617                        print sys.exc_value
618                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
619       
620        slicerpop.AppendSeparator()
621     
622        id = wx.NewId()
[ce64735]623        slicerpop.Append(id, '&sector averaging')
624        wx.EVT_MENU(self, id, self.onSectorSlicer) 
[743d75c]625       
[ce64735]626   
[743d75c]627       
628        pos = event.GetPosition()
629        pos = self.ScreenToClient(pos)
630        self.PopupMenu(slicerpop, pos)
[35d1092]631   
632
[ce64735]633       
634       
Note: See TracBrowser for help on using the repository browser.