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
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
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
18from sans.guicomm.events import StatusEvent ,NewPlotEvent
19
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
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
103        print "model panel name",event.plot.name
104        if event.plot.name in self.plots.keys():
105            # Check whether the class of plottable changed
106            print "model panel",event.plot.name,event.plot.__class__
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 = {}
356        self.data2D= data2d
357        self.data = data2d.data
358        ## Unique ID (from gui_manager)
359        self.uid = None
360       
361        ## Action IDs for internal call-backs
362        self.action_ids = {}
363        self.connect = BindArtist(self.subplot.figure)
364       
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
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()
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()
475        slicerpop.Append(id, '&Toggle Linear/Log scale')
476        wx.EVT_MENU(self, id, self._onToggleScale) 
477         
478        pos = event.GetPosition()
479        pos = self.ScreenToClient(pos)
480        self.PopupMenu(slicerpop, pos)
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       
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()
623        slicerpop.Append(id, '&sector averaging')
624        wx.EVT_MENU(self, id, self.onSectorSlicer) 
625       
626   
627       
628        pos = event.GetPosition()
629        pos = self.ScreenToClient(pos)
630        self.PopupMenu(slicerpop, pos)
631   
632
633       
634       
Note: See TracBrowser for help on using the repository browser.