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

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

change on data_loader

  • Property mode set to 100644
File size: 20.3 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 _onSaveXML(self, path):
423        """
424            Save 1D  Data to  XML file
425           
426            TODO: Refactor and remove this method. See TODO in _onSave.
427           
428            @param evt: Menu event
429        """
430        if not path == None:
431            out = open(path, 'w')
432            from DataLoader.readers import cansas_reader
433            reader = cansas_reader.Reader()
434            data= self.plots[self.graph.selected_plottable]
435            if hasattr(data, "info"):
436                datainfo= data.info
437            else:
438                datainfo= data
439            try:
440                reader.write( path, datainfo)
441            except:
442                msg= "couldn't save point %s"%sys.exc_value
443                raise 
444           
445            try:
446                self._default_save_location = os.path.dirname(path)
447            except:
448                pass
449       
450        return 
451   
452   
453    def _onsaveTXT(self, path):
454        """
455            Save file as txt
456           
457            TODO: Refactor and remove this method. See TODO in _onSave.
458        """
459        data = self.plots[self.graph.selected_plottable]
460       
461        if not path == None:
462            out = open(path, 'w')
463            has_errors = True
464            if data.dy==None or data.dy==[]:
465                has_errors = False
466               
467            # Sanity check
468            if has_errors:
469                try:
470                    if len(data.y) != len(data.dy):
471
472                        has_errors = False
473                except:
474                    has_errors = False
475           
476            if has_errors:
477                out.write("<X>   <Y>   <dY>\n")
478            else:
479                out.write("<X>   <Y>\n")
480               
481            for i in range(len(data.x)):
482                if has_errors:
483                    out.write("%g  %g  %g\n" % (data.x[i], 
484                                                data.y[i],
485                                               data.dy[i]))
486                else:
487                    out.write("%g  %g\n" % (data.x[i], 
488                                            data.y[i]))
489                   
490            out.close()                 
491            try:
492                self._default_save_location = os.path.dirname(path)
493            except:
494                pass   
495               
496    def _onSave(self, evt):
497        """
498            Save a data set to a text file
499            @param evt: Menu event
500        """
501        import os
502        id = str(evt.GetId())
503        if id in self.action_ids:         
504           
505            path = None
506            wildcard = "Text files (*.txt)|*.txt|"\
507            "CanSAS 1D files(*.xml)|*.xml" 
508            dlg = wx.FileDialog(self, "Choose a file",
509                                self._default_save_location, "",wildcard , wx.SAVE)
510           
511            if dlg.ShowModal() == wx.ID_OK:
512                path = dlg.GetPath()
513                mypath = os.path.basename(path)
514               
515                #TODO: This is bad design. The DataLoader is designed to recognize extensions.
516                # It should be a simple matter of calling the .save(file, data, '.xml') method
517                # of the DataLoader.loader.Loader class.
518               
519                if os.path.splitext(mypath)[1].lower() ==".txt":
520                    self._onsaveTXT(path)
521                if os.path.splitext(mypath)[1].lower() ==".xml":
522                    self._onSaveXML(path)
523           
524            dlg.Destroy()
525           
526           
527   
528   
529   
530       
Note: See TracBrowser for help on using the repository browser.