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

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 abf6771 was a85f622, checked in by Jae Cho <jhjcho@…>, 15 years ago

changed and removed "hide error" from context menu for data generated from model plot

  • Property mode set to 100644
File size: 19.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 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
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   
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 numpy.all(selected_plot.dy==0):
245                    id = wx.NewId()
246                    slicerpop.Append(id, '&Show errors')
247                    wx.EVT_MENU(self, id, self._on_add_errors)
248                elif selected_plot.dy !=None and selected_plot.dy != []:
249                    id = wx.NewId()
250                    slicerpop.Append(id, '&Hide Error bars')
251                    wx.EVT_MENU(self, id, self._on_remove_errors)
252           
253            id = wx.NewId()
254            slicerpop.Append(id, '&Linear fit')
255            wx.EVT_MENU(self, id, self.onFitting)
256               
257            slicerpop.AppendSeparator()
258       
259        id = wx.NewId()
260        slicerpop.Append(id, '&Change scale')
261        wx.EVT_MENU(self, id, self._onProperties)
262       
263        id = wx.NewId()
264        slicerpop.Append(id, '&Reset Graph')
265        wx.EVT_MENU(self, id, self.onResetGraph) 
266       
267        pos = event.GetPosition()
268        pos = self.ScreenToClient(pos)
269        self.PopupMenu(slicerpop, pos)
270       
271       
272    def _on_remove_errors(self, evt):
273        """
274            Save name and dy of data in dictionary self.err_dy
275            Create a new data1D with the same x, y
276            vector and dy with zeros.
277            post self.err_dy as event (ErrorDataEvent) for any object
278            which wants to reconstruct the initial data.
279            @param evt: Menu event
280        """
281        if not self.graph.selected_plottable == None:
282            ## store existing dy
283            name =self.plots[self.graph.selected_plottable].name
284            dy = self.plots[self.graph.selected_plottable].dy
285            self.err_dy[name]= dy
286            ## Create a new dy for a new plottable
287            import numpy
288            dy= numpy.zeros(len(self.plots[self.graph.selected_plottable].y))
289            selected_plot= self.plots[self.graph.selected_plottable]
290           
291            if selected_plot.__class__.__name__=="Data1D":
292                # Make sure that we can pass a basic Data1D
293                dxl = None
294                dxw = None
295                if hasattr(selected_plot, "dxl"):
296                    dxl = selected_plot.dxl
297                if hasattr(selected_plot, "dxw"):
298                    dxw = selected_plot.dxw
299                new_plot = Data1D( x=selected_plot.x,
300                              y= selected_plot.y,
301                               dx=selected_plot.dx,
302                              dy=dy)
303                new_plot.dxl  = dxl
304                new_plot.dxw = dxw
305                             
306            else:
307                 new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
308            new_plot.interactive = True
309            self.errors_hide = True
310            new_plot.name = self.plots[self.graph.selected_plottable].name
311            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
312                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
313                new_plot.id = self.plots[self.graph.selected_plottable].id
314            else:
315                new_plot.group_id = str(time.time())
316                new_plot.id = str(time.time())
317            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
318            new_plot.xaxis(label, unit)
319            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
320            new_plot.yaxis(label, unit)
321            ## save the color of the selected plottable before it is deleted
322            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
323            self.graph.delete(self.plots[self.graph.selected_plottable])
324            ## add newly created plottable to the graph with the save color
325            self.graph.color += color
326            self.graph.add(new_plot,color)
327            ## transforming the view of the new data into the same of the previous data
328            self._onEVT_FUNC_PROPERTY()
329            ## save the plot
330            self.plots[self.graph.selected_plottable]=new_plot
331            ## Render the graph
332            self.graph.render(self)
333            self.subplot.figure.canvas.draw_idle() 
334           
335            event = ErrorDataEvent(err_dy=self.err_dy)
336            wx.PostEvent(self.parent, event)
337   
338   
339    def _on_add_errors(self, evt):
340        """
341            create a new data1D witht the errors saved in self.err_dy
342            to show errors of the plot.
343            Compute reasonable errors for a data set without
344            errors and transorm the plottable to a Data1D
345            @param evt: Menu event
346        """
347       
348       
349        if not self.graph.selected_plottable == None \
350            and self.graph.selected_plottable in self.plots.keys():
351            ##Reset the flag to display the hide option on the context menu
352            self.errors_hide = False
353            ## restore dy
354            length = len(self.plots[self.graph.selected_plottable].x)
355            dy = numpy.zeros(length)
356           
357            selected_plot= self.plots[self.graph.selected_plottable]
358           
359            try:
360                dy = self.err_dy[selected_plot.name]
361               
362            except:
363                #for i in range(length):
364                #dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])     
365                if hasattr(selected_plot,"dy"):
366                    dy= selected_plot.dy
367                else:
368                    dy = numpy.zeros(selected_plot.dy)
369                   
370            ## Create a new plottable data1D
371            if selected_plot.__class__.__name__=="Data1D":
372                # Make sure that we can pass a basic Data1D
373                dxl = None
374                dxw = None
375                if hasattr(selected_plot, "dxl"):
376                    dxl = selected_plot.dxl
377                if hasattr(selected_plot, "dxw"):
378                    dxw = selected_plot.dxw
379                new_plot = Data1D( x=selected_plot.x,
380                                               y= selected_plot.y,
381                                               dx=selected_plot.dx,
382                                               dy=dy)
383                new_plot.dxl = dxl
384                new_plot.dxw = dxw
385                                     
386            else:
387                ## Create a new plottable Theory1D
388                new_plot = Theory1D(x=selected_plot.x,y=selected_plot.y,dy=dy)
389           
390            new_plot.interactive = True
391            new_plot.name = self.plots[self.graph.selected_plottable].name
392            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
393                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
394                new_plot.id = self.plots[self.graph.selected_plottable].id
395            else:
396                new_plot.group_id = str(time.time())
397                new_plot.id = str(time.time())
398           
399            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
400            new_plot.xaxis(label, unit)
401            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
402            new_plot.yaxis(label, unit)
403            ## save the color of the selected plottable before it is deleted
404            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
405            self.graph.delete(self.plots[self.graph.selected_plottable])
406            self.graph.color += color
407            ## add newly created plottable to the graph with the save color
408            self.graph.add(new_plot, color)
409            ## transforming the view of the new data into the same of the previous data
410            self._onEVT_FUNC_PROPERTY()
411            ## save the plot
412            self.plots[self.graph.selected_plottable]=new_plot
413            ## render the graph with its new content
414            self.graph.render(self)
415            self.subplot.figure.canvas.draw_idle() 
416               
417               
418    def _onsaveTXT(self, path):
419        """
420            Save file as txt
421           
422            TODO: Refactor and remove this method. See TODO in _onSave.
423        """
424        data = self.plots[self.graph.selected_plottable]
425       
426        if not path == None:
427            out = open(path, 'w')
428            has_errors = True
429            if data.dy==None or data.dy==[]:
430                has_errors = False
431               
432            # Sanity check
433            if has_errors:
434                try:
435                    if len(data.y) != len(data.dy):
436
437                        has_errors = False
438                except:
439                    has_errors = False
440           
441            if has_errors:
442                out.write("<X>   <Y>   <dY>\n")
443            else:
444                out.write("<X>   <Y>\n")
445               
446            for i in range(len(data.x)):
447                if has_errors:
448                    out.write("%g  %g  %g\n" % (data.x[i], 
449                                                data.y[i],
450                                               data.dy[i]))
451                else:
452                    out.write("%g  %g\n" % (data.x[i], 
453                                            data.y[i]))
454                   
455            out.close()                 
456            try:
457                self._default_save_location = os.path.dirname(path)
458            except:
459                pass   
460               
461    def _onSave(self, evt):
462        """
463            Save a data set to a text file
464            @param evt: Menu event
465        """
466       
467        id = str(evt.GetId())
468        if id in self.action_ids:         
469           
470            path = None
471            wildcard = "Text files (*.txt)|*.txt|"\
472            "CanSAS 1D files(*.xml)|*.xml" 
473            dlg = wx.FileDialog(self, "Choose a file",
474                                self._default_save_location, "",wildcard , wx.SAVE)
475           
476            if dlg.ShowModal() == wx.ID_OK:
477                path = dlg.GetPath()
478                mypath = os.path.basename(path)
479               
480                #TODO: This is bad design. The DataLoader is designed to recognize extensions.
481                # It should be a simple matter of calling the .save(file, data, '.xml') method
482                # of the DataLoader.loader.Loader class.
483                from DataLoader.loader import  Loader
484                #Instantiate a loader
485                loader = Loader() 
486                data = self.plots[self.graph.selected_plottable]
487                format=".txt"
488                if os.path.splitext(mypath)[1].lower() == format:
489                     self._onsaveTXT( path)
490
491                format= ".xml"
492                if os.path.splitext(mypath)[1].lower() ==format:
493                    loader.save( path, data, format)
494                try:
495                    self._default_save_location = os.path.dirname(path)
496                except:
497                    pass   
498            dlg.Destroy()
499           
500           
501   
502           
503           
504   
505   
506   
507       
Note: See TracBrowser for help on using the repository browser.