source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 5fe5871c

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

working on combining save option

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