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
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, '&Perform circular average')
456        wx.EVT_MENU(self, id, self.onCircular) 
457       
458        id = wx.NewId()
459        slicerpop.Append(id, '&Sector')
460        wx.EVT_MENU(self, id, self.onSector) 
461       
462       
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()
480        slicerpop.Append(id, '&Toggle Linear/Log scale')
481        wx.EVT_MENU(self, id, self._onToggleScale) 
482         
483        pos = event.GetPosition()
484        pos = self.ScreenToClient(pos)
485        self.PopupMenu(slicerpop, pos)
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()
526    def onCircular(self, event):
527        """
528            perform circular averaging
529        """
530        from DataLoader.manipulations import CircularAverage
531    def onSector(self, event):
532        """
533            Perform sector averaging
534        """
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       
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()
633        slicerpop.Append(id, '&sector averaging')
634        wx.EVT_MENU(self, id, self.onSectorSlicer) 
635       
636   
637       
638        pos = event.GetPosition()
639        pos = self.ScreenToClient(pos)
640        self.PopupMenu(slicerpop, pos)
641   
642
643       
644       
Note: See TracBrowser for help on using the repository browser.