source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ effce1d

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

working on guiframe

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