source: sasview/guiframe/local_perspectives/plotting/DataPanel.py @ b2c3225

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 b2c3225 was 768656e, checked in by Gervaise Alina <gervyh@…>, 16 years ago

fixe plotting more than2 data at the same time from xml file
add another interactor not connect to code yet

  • Property mode set to 100644
File size: 21.9 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()
[768656e]453       
454        id = wx.NewId()
455        slicerpop.Append(id, '&Perform circular average')
456        wx.EVT_MENU(self, id, self.onCircular) 
457       
[743d75c]458        id = wx.NewId()
[b06ef8c]459        slicerpop.Append(id, '&Sector')
460        wx.EVT_MENU(self, id, self.onSector) 
461       
[768656e]462       
[b06ef8c]463     
464       
465        id = wx.NewId()
466        slicerpop.Append(id, '&Clear slicer')
467        wx.EVT_MENU(self, id,  self.onClearSlicer) 
468       
469        id = wx.NewId()
470        slicerpop.Append(id, '&Edit Parameters')
471        wx.EVT_MENU(self, id, self._onEditDetector) 
472       
473        slicerpop.AppendSeparator()
474       
475        id = wx.NewId()
476        slicerpop.Append(id, '&Save image')
477        wx.EVT_MENU(self, id, self.onSaveImage) 
478     
479        id = wx.NewId()
[743d75c]480        slicerpop.Append(id, '&Toggle Linear/Log scale')
481        wx.EVT_MENU(self, id, self._onToggleScale) 
[b06ef8c]482         
[743d75c]483        pos = event.GetPosition()
484        pos = self.ScreenToClient(pos)
485        self.PopupMenu(slicerpop, pos)
[b06ef8c]486   
487    def _setSlicer(self, slicer):
488        # Clear current slicer
489        #printEVT("Plotter2D._setSlicer %s" % slicer)
490       
491        if not self.slicer == None: 
492            self.slicer.clear()           
493           
494        self.slicer_z += 1
495        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
496        self.subplot.set_ylim(-self.qmax, self.qmax)
497        self.subplot.set_xlim(-self.qmax, self.qmax)
498        self.update()
499        self.slicer.update()
500       
501       
502    def get_corrected_data(self):
503        # Protect against empty data set
504        if self.data == None:
505            return None
506        import copy
507        output = copy.deepcopy(self.data)
508        return output
509    def freeze_axes(self):
510        self.axes_frozen = True
511       
512    def thaw_axes(self):
513        self.axes_frozen = False
514       
515    def onMouseMotion(self,event):
516        pass
517    def onWheel(self, event):
518        pass   
519    def update(self, draw=True):
520        """
521            Respond to changes in the model by recalculating the
522            profiles and resetting the widgets.
523        """
524        #self.slicer.update()
525        self.draw()
[768656e]526    def onCircular(self, event):
527        """
528            perform circular averaging
529        """
530        from DataLoader.manipulations import CircularAverage
[b06ef8c]531    def onSector(self, event):
[768656e]532        """
533            Perform sector averaging
534        """
[b06ef8c]535        from SectorSlicer import SectorInteractor
536             
537        self.slicer_z += 1
538        self.slicer = SectorInteractor(self, self.subplot, zorder=self.slicer_z)
539        self.subplot.set_ylim(-self.qmax, self.qmax)
540        self.subplot.set_xlim(-self.qmax, self.qmax)
541        self.update()
542        self.slicer.update()
543       
544       
545    def onClearSlicer(self, event):
546        print "on clear" 
547       
548         
549    def _onEditDetector(self, event):
550        print "on parameter"
551       
552       
[743d75c]553    def _onToggleScale(self, event):
554        """
555            toggle axis and replot image
556        """
557        if self.scale == 'log':
558            self.scale = 'linear'
559        else:
560            self.scale = 'log'
561        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
562                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
563        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
564       
565class View1DModelPanel2D( View1DPanel2D):
566    """
567        Plot panel for use with the GUI manager
568    """
569   
570    ## Internal name for the AUI manager
571    window_name = "plotpanel"
572    ## Title to appear on top of the window
573    window_caption = "Plot Panel"
574    ## Flag to tell the GUI manager that this panel is not
575    #  tied to any perspective
576    ALWAYS_ON = True
577    ## Group ID
578    group_id = None
579   
580    def __init__(self, parent, id = -1,color = None,\
581        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
582        """
583            Initialize the panel
584        """
585        View1DPanel2D.__init__(self, parent, id = id, style = style, **kwargs)
586        ## Reference to the parent window
587        self.parent = parent
588        ## Plottables
589        self.plots = {}
590        print "Model 2d panel"
591       
592        ## Unique ID (from gui_manager)
593        self.uid = None
594       
595        ## Action IDs for internal call-backs
596        self.action_ids = {}
597       
598        ## Graph       
599        self.graph = Graph()
600        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
601        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
602        self.graph.render(self)
603    def onContextMenu(self, event):
604        # Slicer plot popup menu
605         #slicerpop = wx.Menu()
606        slicerpop = PanelMenu()
607        slicerpop.set_plots(self.plots)
608        slicerpop.set_graph(self.graph)
609               
610        # Option to save the data displayed
611   
612        # Various plot options
613        id = wx.NewId()
614        slicerpop.Append(id,'&Save image', 'Save image as PNG')
615        wx.EVT_MENU(self, id, self.onSaveImage)
616       
617       
618        item_list = self.parent.get_context_menu(self.graph)
619        if (not item_list==None) and (not len(item_list)==0):
620                slicerpop.AppendSeparator()
621                for item in item_list:
622                    try:
623                        id = wx.NewId()
624                        slicerpop.Append(id, item[0], item[1])
625                        wx.EVT_MENU(self, id, item[2])
626                    except:
627                        print sys.exc_value
628                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
629       
630        slicerpop.AppendSeparator()
631     
632        id = wx.NewId()
[ce64735]633        slicerpop.Append(id, '&sector averaging')
634        wx.EVT_MENU(self, id, self.onSectorSlicer) 
[743d75c]635       
[ce64735]636   
[743d75c]637       
638        pos = event.GetPosition()
639        pos = self.ScreenToClient(pos)
640        self.PopupMenu(slicerpop, pos)
[35d1092]641   
642
[ce64735]643       
644       
Note: See TracBrowser for help on using the repository browser.