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

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 7342634 was 32c0841, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on pylint

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