source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 837a043

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

append more than one dat into the panel on focus

  • Property mode set to 100644
File size: 20.6 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                new_plot.id = self.plots[self.graph.selected_plottable].id
330            else:
331                new_plot.group_id = str(time.time())
332                new_plot.id = str(time.time())
333            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
334            new_plot.xaxis(label, unit)
335            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
336            new_plot.yaxis(label, unit)
337            ## save the color of the selected plottable before it is deleted
338            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
339            self.graph.delete(self.plots[self.graph.selected_plottable])
340            ## add newly created plottable to the graph with the save color
341            self.graph.color += color
342            self.graph.add(new_plot,color)
343            ## transforming the view of the new data into the same of the previous data
344            self._onEVT_FUNC_PROPERTY()
345            ## save the plot
346            self.plots[self.graph.selected_plottable]=new_plot
347            ## Render the graph
348            self.graph.render(self)
349            self.subplot.figure.canvas.draw_idle() 
350           
351            event = ErrorDataEvent(err_dy=self.err_dy)
352            wx.PostEvent(self.parent, event)
353   
354   
355    def _on_add_errors(self, evt):
356        """
357            create a new data1D witht the errors saved in self.err_dy
358            to show errors of the plot.
359            Compute reasonable errors for a data set without
360            errors and transorm the plottable to a Data1D
361            @param evt: Menu event
362        """
363       
364       
365        if not self.graph.selected_plottable == None \
366            and self.graph.selected_plottable in self.plots.keys():
367            ##Reset the flag to display the hide option on the context menu
368            self.errors_hide = False
369            ## restore dy
370            length = len(self.plots[self.graph.selected_plottable].x)
371            dy = numpy.zeros(length)
372           
373            selected_plot= self.plots[self.graph.selected_plottable]
374           
375            try:
376                dy = self.err_dy[selected_plot.name]
377               
378            except:
379                #for i in range(length):
380                #dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])     
381                if hasattr(selected_plot,"dy"):
382                    dy= selected_plot.dy
383                else:
384                    dy = numpy.zeros(selected_plot.dy)
385                   
386            ## Create a new plottable data1D
387            if selected_plot.__class__.__name__=="Data1D":
388                # Make sure that we can pass a basic Data1D
389                dxl = None
390                dxw = None
391                if hasattr(selected_plot, "dxl"):
392                    dxl = selected_plot.dxl
393                if hasattr(selected_plot, "dxw"):
394                    dxw = selected_plot.dxw
395                new_plot = Data1D( x=selected_plot.x,
396                                               y= selected_plot.y,
397                                               dx=selected_plot.dx,
398                                               dy=dy)
399                new_plot.dxl = dxl
400                new_plot.dxw = dxw
401                                     
402            else:
403                ## Create a new plottable Theory1D
404                new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
405           
406            new_plot.interactive = True
407            new_plot.name = self.plots[self.graph.selected_plottable].name
408            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
409                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
410                new_plot.id = self.plots[self.graph.selected_plottable].id
411            else:
412                new_plot.group_id = str(time.time())
413                new_plot.id = str(time.time())
414           
415            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
416            new_plot.xaxis(label, unit)
417            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
418            new_plot.yaxis(label, unit)
419            ## save the color of the selected plottable before it is deleted
420            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
421            self.graph.delete(self.plots[self.graph.selected_plottable])
422            self.graph.color += color
423            ## add newly created plottable to the graph with the save color
424            self.graph.add(new_plot, color)
425            ## transforming the view of the new data into the same of the previous data
426            self._onEVT_FUNC_PROPERTY()
427            ## save the plot
428            self.plots[self.graph.selected_plottable]=new_plot
429            ## render the graph with its new content
430            self.graph.render(self)
431            self.subplot.figure.canvas.draw_idle() 
432               
433               
434    def _onsaveTXT(self, path):
435        """
436            Save file as txt
437           
438            TODO: Refactor and remove this method. See TODO in _onSave.
439        """
440        data = self.plots[self.graph.selected_plottable]
441       
442        if not path == None:
443            out = open(path, 'w')
444            has_errors = True
445            if data.dy==None or data.dy==[]:
446                has_errors = False
447               
448            # Sanity check
449            if has_errors:
450                try:
451                    if len(data.y) != len(data.dy):
452
453                        has_errors = False
454                except:
455                    has_errors = False
456           
457            if has_errors:
458                out.write("<X>   <Y>   <dY>\n")
459            else:
460                out.write("<X>   <Y>\n")
461               
462            for i in range(len(data.x)):
463                if has_errors:
464                    out.write("%g  %g  %g\n" % (data.x[i], 
465                                                data.y[i],
466                                               data.dy[i]))
467                else:
468                    out.write("%g  %g\n" % (data.x[i], 
469                                            data.y[i]))
470                   
471            out.close()                 
472            try:
473                self._default_save_location = os.path.dirname(path)
474            except:
475                pass   
476               
477    def _onSave(self, evt):
478        """
479            Save a data set to a text file
480            @param evt: Menu event
481        """
482       
483        id = str(evt.GetId())
484        if id in self.action_ids:         
485           
486            path = None
487            wildcard = "Text files (*.txt)|*.txt|"\
488            "CanSAS 1D files(*.xml)|*.xml" 
489            dlg = wx.FileDialog(self, "Choose a file",
490                                self._default_save_location, "",wildcard , wx.SAVE)
491           
492            if dlg.ShowModal() == wx.ID_OK:
493                path = dlg.GetPath()
494                mypath = os.path.basename(path)
495               
496                #TODO: This is bad design. The DataLoader is designed to recognize extensions.
497                # It should be a simple matter of calling the .save(file, data, '.xml') method
498                # of the DataLoader.loader.Loader class.
499                from DataLoader.loader import  Loader
500                #Instantiate a loader
501                loader = Loader() 
502                data = self.plots[self.graph.selected_plottable]
503                format=".txt"
504                if os.path.splitext(mypath)[1].lower() == format:
505                     self._onsaveTXT( path)
506
507                format= ".xml"
508                if os.path.splitext(mypath)[1].lower() ==format:
509                    loader.save( path, data, format)
510                try:
511                    self._default_save_location = os.path.dirname(path)
512                except:
513                    pass   
514            dlg.Destroy()
515           
516           
517   
518           
519           
520   
521   
522   
523       
Note: See TracBrowser for help on using the repository browser.