source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 0f3fefe

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

remove save as xml function

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