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

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

change open in menu file to load data

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