source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 340c2b3

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 340c2b3 was 691643c, checked in by Gervaise Alina <gervyh@…>, 14 years ago

make panels inheriting from panelbase

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