source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 588f84f

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 588f84f was 1debb29, checked in by Gervaise Alina <gervyh@…>, 15 years ago

move code of panel slicer from guiframe to fitting plugin

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