source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 7e4a6ff

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 7e4a6ff was d955bf19, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on documentation

  • Property mode set to 100644
File size: 20.1 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
11
12
13import wx
14import sys
15import os
16import pylab
17import math
18import numpy
19import time
20
21import danse.common.plottools
22from danse.common.plottools.PlotPanel import PlotPanel
23from danse.common.plottools.plottables import Graph
24from sans.guiframe import dataFitting
25from sans.guicomm.events import EVT_NEW_PLOT
26from sans.guicomm.events import StatusEvent ,NewPlotEvent,SlicerEvent,ErrorDataEvent
27from sans.guicomm.events import RemoveDataEvent, AddManyDataEvent
28from sans.guiframe.utils import PanelMenu
29from sans.guiframe.dataFitting import Data1D
30from sans.guiframe.dataFitting import Theory1D
31from binder import BindArtist
32
33
34DEFAULT_QMAX = 0.05
35DEFAULT_QSTEP = 0.001
36DEFAULT_BEAM = 0.005
37BIN_WIDTH =1
38
39
40class ModelPanel1D(PlotPanel):
41    """
42    Plot panel for use with the GUI manager
43    """
44   
45    ## Internal name for the AUI manager
46    window_name = "plotpanel"
47    ## Title to appear on top of the window
48    window_caption = "Plot Panel"
49    ## Flag to tell the GUI manager that this panel is not
50    #  tied to any perspective
51    ALWAYS_ON = True
52    ## Group ID
53    group_id = None
54   
55    def __init__(self, parent, id = -1, color = None,\
56        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
57        """
58        Initialize the panel
59        """
60        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
61       
62        ## Reference to the parent window
63        self.parent = parent
64        ## Plottables
65        self.plots = {}
66        ## save errors dy  for each data plotted
67        self.err_dy={}
68        ## flag to determine if the hide or show context menu item should
69        ## be displayed
70        self.errors_hide=False
71        ## Unique ID (from gui_manager)
72        self.uid = None
73        ## Action IDs for internal call-backs
74        self.action_ids = {}
75        ## Default locations
76        self._default_save_location = os.getcwd()       
77        ## Graph       
78        self.graph = Graph()
79        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
80        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
81        self.graph.render(self)
82   
83    def _reset(self):
84        """
85        Resets internal data and graph
86        """   
87        self.graph.reset()
88        self.plots      = {}
89        self.action_ids = {}
90   
91    def _onEVT_1DREPLOT(self, event):
92        """
93        Data is ready to be displayed
94       
95        :param event: data event
96       
97        """
98        #TODO: Check for existence of plot attribute
99        # Check whether this is a replot. If we ask for a replot
100        # and the plottable no longer exists, ignore the event.
101        if hasattr(event, "update") and event.update==True \
102            and event.plot.name not in self.plots.keys():
103            return
104       
105        if hasattr(event, "reset"):
106            self._reset()
107       
108        # Check whether the plottable is empty
109        is_empty = len(event.plot.x)==0
110               
111        is_new = True
112        if event.plot.name in self.plots.keys():
113            # If the plottable is empty, just remove the plottable from the graph
114            if is_empty:
115                self.graph.delete(self.plots[event.plot.name])
116                del self.plots[event.plot.name]
117            else: 
118                # Check whether the class of plottable changed
119                if not event.plot.__class__==self.plots[event.plot.name].__class__:
120                    #overwrite a plottable using the same name
121                    self.graph.delete(self.plots[event.plot.name])             
122                else:
123                    # plottable is already draw on the panel
124                    is_new = False
125       
126        if not is_empty:
127            if is_new:
128                # a new plottable overwrites a plotted one  using the same id
129                for plottable in self.plots.itervalues():
130                    if hasattr(event.plot,"id") and hasattr(plottable, "id"):
131                        #remove the existing plot and same id and same name
132                        if event.plot.id==plottable.id and event.plot.name== plottable.name:
133                            self.graph.delete(plottable)
134               
135                self.plots[event.plot.name] = event.plot
136                self.graph.add(self.plots[event.plot.name])
137            else:
138                #replot the graph
139                self.plots[event.plot.name].x = event.plot.x   
140                self.plots[event.plot.name].y = event.plot.y   
141                self.plots[event.plot.name].dy = event.plot.dy 
142                if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
143                    self.plots[event.plot.name].dx = event.plot.dx   
144         
145        #TODO: Should re-factor this
146        ## for all added plot the option to hide error show be displayed first
147        #self.errors_hide = 0
148        ## Set axis labels
149        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
150        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
151        ## Set the view scale for all plots
152        self._onEVT_FUNC_PROPERTY()
153        ## render the graph
154        self.graph.render(self)
155        self.subplot.figure.canvas.draw_idle()
156        #if self.errors_hide:
157        #    self._on_remove_errors(evt=None)
158        #else:
159        #    self._on_add_errors( evt=None)
160   
161    def onLeftDown(self,event): 
162        """
163        left button down and ready to drag
164        Display the position of the mouse on the statusbar
165        """
166        PlotPanel.onLeftDown(self, event)
167        ax = event.inaxes
168        if ax != None:
169            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
170            wx.PostEvent(self.parent, StatusEvent(status=position))
171           
172        #post nd event to notify guiframe that this panel is on focus
173        wx.PostEvent(self.parent, AddManyDataEvent(panel=self))
174       
175    def _onRemove(self, event):
176        """
177        Remove a plottable from the graph and render the graph
178       
179        :param event: Menu event
180       
181        """
182        ## Check if there is a selected graph to remove
183        if not self.graph.selected_plottable == None and\
184            self.graph.selected_plottable in self.plots.keys():
185            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
186           
187            event = RemoveDataEvent(data =self.plots[self.graph.selected_plottable])
188            wx.PostEvent(self.parent, event)
189            self.graph.delete(self.plots[self.graph.selected_plottable])
190            del self.plots[self.graph.selected_plottable]
191            ## increment graph color
192            self.graph.color += color
193            self.graph.render(self)
194            self.subplot.figure.canvas.draw_idle()   
195           
196    def onContextMenu(self, event):
197        """
198        1D plot context menu
199       
200        :param event: wx context event
201       
202        """
203        slicerpop = PanelMenu()
204        slicerpop.set_plots(self.plots)
205        slicerpop.set_graph(self.graph)
206               
207        # Various plot options
208        id = wx.NewId()
209        slicerpop.Append(id,'&Save image', 'Save image as PNG')
210        wx.EVT_MENU(self, id, self.onSaveImage)
211       
212        id = wx.NewId()
213        slicerpop.Append(id,'&Print image', 'Print image ')
214        wx.EVT_MENU(self, id, self.onPrint)
215         
216        id = wx.NewId()
217        slicerpop.Append(id,'&Print Preview', 'image preview for print')
218        wx.EVT_MENU(self, id, self.onPrinterPreview)
219           
220        slicerpop.AppendSeparator()
221        item_list = self.parent.get_context_menu(self.graph)
222       
223        if (not item_list==None) and (not len(item_list)==0):
224            for item in item_list:
225                try:
226                    id = wx.NewId()
227                    slicerpop.Append(id, item[0], item[1])
228                    wx.EVT_MENU(self, id, item[2])
229                except:
230                    wx.PostEvent(self.parent, StatusEvent(status=\
231                        "ModelPanel1D.onContextMenu: bad menu item  %s"%sys.exc_value))
232                    pass
233            slicerpop.AppendSeparator()
234       
235        if self.graph.selected_plottable in self.plots:
236            plot = self.plots[self.graph.selected_plottable]
237            id = wx.NewId()
238            name = plot.name
239           
240            slicerpop.Append(id, "&Save points" )
241            self.action_ids[str(id)] = plot
242            wx.EVT_MENU(self, id, self._onSave)
243         
244            id = wx.NewId()
245            slicerpop.Append(id, "Remove %s curve" % name)
246            self.action_ids[str(id)] = plot
247            wx.EVT_MENU(self, id, self._onRemove)
248            slicerpop.AppendSeparator()
249            # Option to hide
250            #TODO: implement functionality to hide a plottable (legend click)
251       
252        if self.graph.selected_plottable in self.plots:
253            selected_plot= self.plots[self.graph.selected_plottable]
254           
255            id = wx.NewId()
256            slicerpop.Append(id, '&Linear fit')
257            wx.EVT_MENU(self, id, self.onFitting)
258               
259            slicerpop.AppendSeparator()
260       
261        id = wx.NewId()
262        slicerpop.Append(id, '&Change scale')
263        wx.EVT_MENU(self, id, self._onProperties)
264       
265        id = wx.NewId()
266        slicerpop.Append(id, '&Reset Graph')
267        wx.EVT_MENU(self, id, self.onResetGraph) 
268       
269        pos = event.GetPosition()
270        pos = self.ScreenToClient(pos)
271        self.PopupMenu(slicerpop, pos)
272       
273       
274    def _on_remove_errors(self, evt):
275        """
276        Save name and dy of data in dictionary self.err_dy
277        Create a new data1D with the same x, y
278        vector and dy with zeros.
279        post self.err_dy as event (ErrorDataEvent) for any object
280        which wants to reconstruct the initial data.
281       
282        :param evt: Menu event
283       
284        """
285        if not self.graph.selected_plottable == None:
286            ## store existing dy
287            name =self.plots[self.graph.selected_plottable].name
288            dy = self.plots[self.graph.selected_plottable].dy
289            self.err_dy[name]= dy
290            ## Create a new dy for a new plottable
291            import numpy
292            dy = numpy.zeros(len(self.plots[self.graph.selected_plottable].y))
293            selected_plot = self.plots[self.graph.selected_plottable]
294           
295            if selected_plot.__class__.__name__=="Data1D":
296                # Make sure that we can pass a basic Data1D
297                dxl = None
298                dxw = None
299                if hasattr(selected_plot, "dxl"):
300                    dxl = selected_plot.dxl
301                if hasattr(selected_plot, "dxw"):
302                    dxw = selected_plot.dxw
303                new_plot = Data1D( x=selected_plot.x,
304                              y= selected_plot.y,
305                               dx=selected_plot.dx,
306                              dy=dy)
307                new_plot.dxl  = dxl
308                new_plot.dxw = dxw
309                             
310            else:
311                 new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
312            new_plot.interactive = True
313            self.errors_hide = True
314            new_plot.name = self.plots[self.graph.selected_plottable].name
315            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
316                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
317                if hasattr(self.plots[self.graph.selected_plottable],"id"):
318                    new_plot.id = self.plots[self.graph.selected_plottable].id
319                else:
320                    new_plot.id = str(time.time())
321            else:
322                new_plot.group_id = str(time.time())
323                new_plot.id = str(time.time())
324            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
325            new_plot.xaxis(label, unit)
326            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
327            new_plot.yaxis(label, unit)
328            ## save the color of the selected plottable before it is deleted
329            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
330            self.graph.delete(self.plots[self.graph.selected_plottable])
331            ## add newly created plottable to the graph with the save color
332            self.graph.color += color
333            self.graph.add(new_plot,color)
334            ## transforming the view of the new data into the same of the previous data
335            self._onEVT_FUNC_PROPERTY()
336            ## save the plot
337            self.plots[self.graph.selected_plottable]=new_plot
338            ## Render the graph
339            self.graph.render(self)
340            self.subplot.figure.canvas.draw_idle() 
341           
342            event = ErrorDataEvent(err_dy=self.err_dy)
343            wx.PostEvent(self.parent, event)
344
345    def _on_add_errors(self, evt):
346        """
347        create a new data1D witht the errors saved in self.err_dy
348        to show errors of the plot.
349        Compute reasonable errors for a data set without
350        errors and transorm the plottable to a Data1D
351       
352        :param evt: Menu event
353       
354        """
355        if not self.graph.selected_plottable == None \
356            and self.graph.selected_plottable in self.plots.keys():
357            ##Reset the flag to display the hide option on the context menu
358            self.errors_hide = False
359            ## restore dy
360            length = len(self.plots[self.graph.selected_plottable].x)
361            dy = numpy.zeros(length)
362           
363            selected_plot= self.plots[self.graph.selected_plottable]
364           
365            try:
366                dy = self.err_dy[selected_plot.name]
367               
368            except:
369                #for i in range(length):
370                #dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])     
371                if hasattr(selected_plot,"dy"):
372                    dy= selected_plot.dy
373                else:
374                    dy = numpy.zeros(selected_plot.dy)
375                   
376            ## Create a new plottable data1D
377            if selected_plot.__class__.__name__=="Data1D":
378                # Make sure that we can pass a basic Data1D
379                dxl = None
380                dxw = None
381                if hasattr(selected_plot, "dxl"):
382                    dxl = selected_plot.dxl
383                if hasattr(selected_plot, "dxw"):
384                    dxw = selected_plot.dxw
385                new_plot = Data1D( x=selected_plot.x,
386                                               y= selected_plot.y,
387                                               dx=selected_plot.dx,
388                                               dy=dy)
389                new_plot.dxl = dxl
390                new_plot.dxw = dxw
391                                     
392            else:
393                ## Create a new plottable Theory1D
394                new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
395           
396            new_plot.interactive = True
397            new_plot.name = self.plots[self.graph.selected_plottable].name
398            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
399                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
400                if hasattr(self.plots[self.graph.selected_plottable],"id"):
401                    new_plot.id = self.plots[self.graph.selected_plottable].id
402                else:
403                    new_plot.id = str(time.time())
404            else:
405                new_plot.group_id = str(time.time())
406                new_plot.id = str(time.time())
407           
408            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
409            new_plot.xaxis(label, unit)
410            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
411            new_plot.yaxis(label, unit)
412            ## save the color of the selected plottable before it is deleted
413            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
414            self.graph.delete(self.plots[self.graph.selected_plottable])
415            self.graph.color += color
416            ## add newly created plottable to the graph with the save color
417            self.graph.add(new_plot, color)
418            ## transforming the view of the new data into the same of the previous data
419            self._onEVT_FUNC_PROPERTY()
420            ## save the plot
421            self.plots[self.graph.selected_plottable]=new_plot
422            ## render the graph with its new content
423            self.graph.render(self)
424            self.subplot.figure.canvas.draw_idle() 
425               
426               
427    def _onsaveTXT(self, path):
428        """
429        Save file as txt
430           
431        :TODO: Refactor and remove this method. See TODO in _onSave.
432       
433        """
434        data = self.plots[self.graph.selected_plottable]
435       
436        if not path == None:
437            out = open(path, 'w')
438            has_errors = True
439            if data.dy==None or data.dy==[]:
440                has_errors = False
441               
442            # Sanity check
443            if has_errors:
444                try:
445                    if len(data.y) != len(data.dy):
446
447                        has_errors = False
448                except:
449                    has_errors = False
450           
451            if has_errors:
452                out.write("<X>   <Y>   <dY>\n")
453            else:
454                out.write("<X>   <Y>\n")
455               
456            for i in range(len(data.x)):
457                if has_errors:
458                    out.write("%g  %g  %g\n" % (data.x[i], 
459                                                data.y[i],
460                                               data.dy[i]))
461                else:
462                    out.write("%g  %g\n" % (data.x[i], 
463                                            data.y[i]))
464                   
465            out.close()                 
466            try:
467                self._default_save_location = os.path.dirname(path)
468            except:
469                pass   
470               
471    def _onSave(self, evt):
472        """
473        Save a data set to a text file
474       
475        :param evt: Menu event
476       
477        """
478        id = str(evt.GetId())
479        if id in self.action_ids:         
480           
481            path = None
482            wildcard = "Text files (*.txt)|*.txt|"\
483            "CanSAS 1D files(*.xml)|*.xml" 
484            dlg = wx.FileDialog(self, "Choose a file",
485                                self._default_save_location, "",wildcard , wx.SAVE)
486           
487            if dlg.ShowModal() == wx.ID_OK:
488                path = dlg.GetPath()
489                mypath = os.path.basename(path)
490               
491                #TODO: This is bad design. The DataLoader is designed to recognize extensions.
492                # It should be a simple matter of calling the .save(file, data, '.xml') method
493                # of the DataLoader.loader.Loader class.
494                from DataLoader.loader import  Loader
495                #Instantiate a loader
496                loader = Loader() 
497                data = self.plots[self.graph.selected_plottable]
498                format=".txt"
499                if os.path.splitext(mypath)[1].lower() == format:
500                     self._onsaveTXT( path)
501
502                format= ".xml"
503                if os.path.splitext(mypath)[1].lower() ==format:
504                    loader.save( path, data, format)
505                try:
506                    self._default_save_location = os.path.dirname(path)
507                except:
508                    pass   
509            dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.