source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 74755ff

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 74755ff was 0b57a57, checked in by Gervaise Alina <gervyh@…>, 14 years ago

make sure one can append more than 1 model for fitting

  • Property mode set to 100644
File size: 20.7 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 os
15import pylab
16import math
17import numpy
18import time
19
20import danse.common.plottools
21from danse.common.plottools.PlotPanel import PlotPanel
22from danse.common.plottools.plottables import Graph
23from sans.guiframe import dataFitting
24from sans.guicomm.events import EVT_NEW_PLOT
25from sans.guicomm.events import StatusEvent ,NewPlotEvent,SlicerEvent,ErrorDataEvent
26from sans.guicomm.events import RemoveDataEvent, AddManyDataEvent
27from sans.guiframe.utils import PanelMenu
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.dataFitting import Theory1D
30from binder import BindArtist
31
32
33DEFAULT_QMAX = 0.05
34DEFAULT_QSTEP = 0.001
35DEFAULT_BEAM = 0.005
36BIN_WIDTH =1
37
38
39class ModelPanel1D(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        ## save errors dy  for each data plotted
66        self.err_dy={}
67        ## flag to determine if the hide or show context menu item should
68        ## be displayed
69        self.errors_hide=False
70        ## Unique ID (from gui_manager)
71        self.uid = None
72        ## Action IDs for internal call-backs
73        self.action_ids = {}
74        ## Default locations
75        self._default_save_location = os.getcwd()       
76        ## Graph       
77        self.graph = Graph()
78        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
79        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
80        self.graph.render(self)
81   
82    def _reset(self):
83        """
84            Resets internal data and graph
85        """   
86        self.graph.reset()
87        self.plots      = {}
88        self.action_ids = {}
89   
90    def _onEVT_1DREPLOT(self, event):
91        """
92            Data is ready to be displayed
93            @param event: data event
94        """
95       
96        #TODO: Check for existence of plot attribute
97        # Check whether this is a replot. If we ask for a replot
98        # and the plottable no longer exists, ignore the event.
99        if hasattr(event, "update") and event.update==True \
100            and event.plot.name not in self.plots.keys():
101            return
102       
103        if hasattr(event, "reset"):
104            self._reset()
105       
106        # Check whether the plottable is empty
107        is_empty = len(event.plot.x)==0
108               
109        is_new = True
110        if event.plot.name in self.plots.keys():
111            # If the plottable is empty, just remove the plottable from the graph
112            if is_empty:
113                self.graph.delete(self.plots[event.plot.name])
114                del self.plots[event.plot.name]
115            else: 
116                # Check whether the class of plottable changed
117                if not event.plot.__class__==self.plots[event.plot.name].__class__:
118                    #overwrite a plottable using the same name
119                    self.graph.delete(self.plots[event.plot.name])             
120                else:
121                    # plottable is already draw on the panel
122                    is_new = False
123       
124        if not is_empty:
125            if is_new:
126                # a new plottable overwrites a plotted one  using the same id
127                for plottable in self.plots.itervalues():
128                    if hasattr(event.plot,"id") and hasattr(plottable, "id"):
129                        #remove the existing plot and same id and same name
130                        if event.plot.id==plottable.id and event.plot.name== plottable.name:
131                            self.graph.delete(plottable)
132               
133                self.plots[event.plot.name] = event.plot
134                self.graph.add(self.plots[event.plot.name])
135            else:
136                #replot the graph
137                self.plots[event.plot.name].x = event.plot.x   
138                self.plots[event.plot.name].y = event.plot.y   
139                self.plots[event.plot.name].dy = event.plot.dy 
140                if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
141                    self.plots[event.plot.name].dx = event.plot.dx   
142         
143        #TODO: Should re-factor this
144        ## for all added plot the option to hide error show be displayed first
145        #self.errors_hide = 0
146        ## Set axis labels
147        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
148        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
149        ## Set the view scale for all plots
150        self._onEVT_FUNC_PROPERTY()
151        ## render the graph
152        self.graph.render(self)
153        self.subplot.figure.canvas.draw_idle()
154        #if self.errors_hide:
155        #    self._on_remove_errors(evt=None)
156        #else:
157        #    self._on_add_errors( evt=None)
158   
159    def onLeftDown(self,event): 
160        """
161            left button down and ready to drag
162            Display the position of the mouse on the statusbar
163        """
164        PlotPanel.onLeftDown(self, event)
165        ax = event.inaxes
166        if ax != None:
167            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
168            wx.PostEvent(self.parent, StatusEvent(status=position))
169           
170        #post nd event to notify guiframe that this panel is on focus
171        wx.PostEvent(self.parent, AddManyDataEvent(panel=self))
172       
173    def _onRemove(self, event):
174        """
175            Remove a plottable from the graph and render the graph
176            @param event: Menu event
177        """
178        ## Check if there is a selected graph to remove
179        if not self.graph.selected_plottable == None and\
180            self.graph.selected_plottable in self.plots.keys():
181            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
182           
183            event = RemoveDataEvent(data =self.plots[self.graph.selected_plottable])
184            wx.PostEvent(self.parent, event)
185            self.graph.delete(self.plots[self.graph.selected_plottable])
186            del self.plots[self.graph.selected_plottable]
187            ## increment graph color
188            self.graph.color += color
189            self.graph.render(self)
190            self.subplot.figure.canvas.draw_idle()   
191           
192           
193
194    def onContextMenu(self, event):
195        """
196            1D plot context menu
197            @param event: wx context event
198        """
199        slicerpop = PanelMenu()
200        slicerpop.set_plots(self.plots)
201        slicerpop.set_graph(self.graph)
202               
203        # Various plot options
204        id = wx.NewId()
205        slicerpop.Append(id,'&Save image', 'Save image as PNG')
206        wx.EVT_MENU(self, id, self.onSaveImage)
207       
208        id = wx.NewId()
209        slicerpop.Append(id,'&Print image', 'Print image ')
210        wx.EVT_MENU(self, id, self.onPrint)
211         
212        id = wx.NewId()
213        slicerpop.Append(id,'&Print Preview', 'image preview for print')
214        wx.EVT_MENU(self, id, self.onPrinterPreview)
215           
216        slicerpop.AppendSeparator()
217        item_list = self.parent.get_context_menu(self.graph)
218       
219        if (not item_list==None) and (not len(item_list)==0):
220            for item in item_list:
221                try:
222                    id = wx.NewId()
223                    slicerpop.Append(id, item[0], item[1])
224                    wx.EVT_MENU(self, id, item[2])
225                except:
226                    wx.PostEvent(self.parent, StatusEvent(status=\
227                        "ModelPanel1D.onContextMenu: bad menu item  %s"%sys.exc_value))
228                    pass
229            slicerpop.AppendSeparator()
230       
231        if self.graph.selected_plottable in self.plots:
232            plot = self.plots[self.graph.selected_plottable]
233            id = wx.NewId()
234            name = plot.name
235           
236            slicerpop.Append(id, "&Save points" )
237            self.action_ids[str(id)] = plot
238            wx.EVT_MENU(self, id, self._onSave)
239         
240            id = wx.NewId()
241            slicerpop.Append(id, "Remove %s curve" % name)
242            self.action_ids[str(id)] = plot
243            wx.EVT_MENU(self, id, self._onRemove)
244            slicerpop.AppendSeparator()
245            # Option to hide
246            #TODO: implement functionality to hide a plottable (legend click)
247       
248        if self.graph.selected_plottable in self.plots:
249            selected_plot= self.plots[self.graph.selected_plottable]
250            #if self.plots[self.graph.selected_plottable].name in self.err_dy.iterkeys()\
251            #    and self.errors_hide:
252            """
253            if selected_plot.__class__.__name__=="Data1D":
254                if numpy.all(selected_plot.dy==0):
255                    id = wx.NewId()
256                    slicerpop.Append(id, '&Show errors')
257                    wx.EVT_MENU(self, id, self._on_add_errors)
258                elif selected_plot.dy !=None and selected_plot.dy != []:
259                    id = wx.NewId()
260                    slicerpop.Append(id, '&Hide Error bars')
261                    wx.EVT_MENU(self, id, self._on_remove_errors)
262            """
263            id = wx.NewId()
264            slicerpop.Append(id, '&Linear fit')
265            wx.EVT_MENU(self, id, self.onFitting)
266               
267            slicerpop.AppendSeparator()
268       
269        id = wx.NewId()
270        slicerpop.Append(id, '&Change scale')
271        wx.EVT_MENU(self, id, self._onProperties)
272       
273        id = wx.NewId()
274        slicerpop.Append(id, '&Reset Graph')
275        wx.EVT_MENU(self, id, self.onResetGraph) 
276       
277        pos = event.GetPosition()
278        pos = self.ScreenToClient(pos)
279        self.PopupMenu(slicerpop, pos)
280       
281       
282    def _on_remove_errors(self, evt):
283        """
284            Save name and dy of data in dictionary self.err_dy
285            Create a new data1D with the same x, y
286            vector and dy with zeros.
287            post self.err_dy as event (ErrorDataEvent) for any object
288            which wants to reconstruct the initial data.
289            @param evt: Menu event
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   
352    def _on_add_errors(self, evt):
353        """
354            create a new data1D witht the errors saved in self.err_dy
355            to show errors of the plot.
356            Compute reasonable errors for a data set without
357            errors and transorm the plottable to a Data1D
358            @param evt: Menu event
359        """
360       
361       
362        if not self.graph.selected_plottable == None \
363            and self.graph.selected_plottable in self.plots.keys():
364            ##Reset the flag to display the hide option on the context menu
365            self.errors_hide = False
366            ## restore dy
367            length = len(self.plots[self.graph.selected_plottable].x)
368            dy = numpy.zeros(length)
369           
370            selected_plot= self.plots[self.graph.selected_plottable]
371           
372            try:
373                dy = self.err_dy[selected_plot.name]
374               
375            except:
376                #for i in range(length):
377                #dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])     
378                if hasattr(selected_plot,"dy"):
379                    dy= selected_plot.dy
380                else:
381                    dy = numpy.zeros(selected_plot.dy)
382                   
383            ## Create a new plottable data1D
384            if selected_plot.__class__.__name__=="Data1D":
385                # Make sure that we can pass a basic Data1D
386                dxl = None
387                dxw = None
388                if hasattr(selected_plot, "dxl"):
389                    dxl = selected_plot.dxl
390                if hasattr(selected_plot, "dxw"):
391                    dxw = selected_plot.dxw
392                new_plot = Data1D( x=selected_plot.x,
393                                               y= selected_plot.y,
394                                               dx=selected_plot.dx,
395                                               dy=dy)
396                new_plot.dxl = dxl
397                new_plot.dxw = dxw
398                                     
399            else:
400                ## Create a new plottable Theory1D
401                new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
402           
403            new_plot.interactive = True
404            new_plot.name = self.plots[self.graph.selected_plottable].name
405            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
406                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
407                if hasattr(self.plots[self.graph.selected_plottable],"id"):
408                    new_plot.id = self.plots[self.graph.selected_plottable].id
409                else:
410                    new_plot.id = str(time.time())
411            else:
412                new_plot.group_id = str(time.time())
413                new_plot.id = str(time.time())
414           
415            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
416            new_plot.xaxis(label, unit)
417            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
418            new_plot.yaxis(label, unit)
419            ## save the color of the selected plottable before it is deleted
420            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
421            self.graph.delete(self.plots[self.graph.selected_plottable])
422            self.graph.color += color
423            ## add newly created plottable to the graph with the save color
424            self.graph.add(new_plot, color)
425            ## transforming the view of the new data into the same of the previous data
426            self._onEVT_FUNC_PROPERTY()
427            ## save the plot
428            self.plots[self.graph.selected_plottable]=new_plot
429            ## render the graph with its new content
430            self.graph.render(self)
431            self.subplot.figure.canvas.draw_idle() 
432               
433               
434    def _onsaveTXT(self, path):
435        """
436            Save file as txt
437           
438            TODO: Refactor and remove this method. See TODO in _onSave.
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            @param evt: Menu event
481        """
482       
483        id = str(evt.GetId())
484        if id in self.action_ids:         
485           
486            path = None
487            wildcard = "Text files (*.txt)|*.txt|"\
488            "CanSAS 1D files(*.xml)|*.xml" 
489            dlg = wx.FileDialog(self, "Choose a file",
490                                self._default_save_location, "",wildcard , wx.SAVE)
491           
492            if dlg.ShowModal() == wx.ID_OK:
493                path = dlg.GetPath()
494                mypath = os.path.basename(path)
495               
496                #TODO: This is bad design. The DataLoader is designed to recognize extensions.
497                # It should be a simple matter of calling the .save(file, data, '.xml') method
498                # of the DataLoader.loader.Loader class.
499                from DataLoader.loader import  Loader
500                #Instantiate a loader
501                loader = Loader() 
502                data = self.plots[self.graph.selected_plottable]
503                format=".txt"
504                if os.path.splitext(mypath)[1].lower() == format:
505                     self._onsaveTXT( path)
506
507                format= ".xml"
508                if os.path.splitext(mypath)[1].lower() ==format:
509                    loader.save( path, data, format)
510                try:
511                    self._default_save_location = os.path.dirname(path)
512                except:
513                    pass   
514            dlg.Destroy()
515           
516           
517   
518           
519           
520   
521   
522   
523       
Note: See TracBrowser for help on using the repository browser.