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

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

remember the previous directory for save option

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