source: sasview/guitools/PlotPanel.py @ e2914b1

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 e2914b1 was e2914b1, checked in by Gervaise Alina <gervyh@…>, 16 years ago

almost worling .can display both files but cannot trans form x to log x because of Log zero .still thinking…but everything else is working…Mathieu can you take a look at all my error function for the special cases such as 1/zero

  • Property mode set to 100644
File size: 17.7 KB
Line 
1import wx.lib.newevent
2import matplotlib
3matplotlib.interactive(False)
4#Use the WxAgg back end. The Wx one takes too long to render
5matplotlib.use('WXAgg')
6from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
7from matplotlib.figure import Figure
8import os
9import fittings
10from canvas import FigureCanvas
11#TODO: make the plottables interactive
12
13from plottables import Graph
14#(FuncFitEvent, EVT_FUNC_FIT) = wx.lib.newevent.NewEvent()
15import math,pylab
16def show_tree(obj,d=0):
17    """Handy function for displaying a tree of graph objects"""
18    print "%s%s" % ("-"*d,obj.__class__.__name__)
19    if 'get_children' in dir(obj):
20        for a in obj.get_children(): show_tree(a,d+1)
21
22
23
24class PlotPanel(wx.Panel):
25    """
26    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
27    flag, and the actually redrawing of the
28    figure is triggered by an Idle event.
29    """
30    def __init__(self, parent, id = -1, color = None,\
31        dpi = None, **kwargs):
32        wx.Panel.__init__(self, parent, id = id, **kwargs)
33        self.parent = parent
34        self.figure = Figure(None, dpi)
35        #self.figure = pylab.Figure(None, dpi)
36        #self.canvas = NoRepaintCanvas(self, -1, self.figure)
37        self.canvas = FigureCanvas(self, -1, self.figure)
38        self.SetColor(color)
39        #self.Bind(wx.EVT_IDLE, self._onIdle)
40        #self.Bind(wx.EVT_SIZE, self._onSize)
41        self._resizeflag = True
42        self._SetSize()
43        self.subplot = self.figure.add_subplot(111)
44        self.figure.subplots_adjust(left=.2, bottom=.2)
45        self.yscale = 'linear'
46        self.xscale = 'linear'
47        sizer = wx.BoxSizer(wx.VERTICAL)
48        sizer.Add(self.canvas,1,wx.EXPAND)
49        self.SetSizer(sizer)
50
51        # Graph object to manage the plottables
52        self.graph = Graph()
53        #self.Bind(EVT_FUNC_FIT, self.onFitRange)
54        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
55        #self.Bind(EVT_PROPERTY, self._onEVT_FUNC_PROPERTY)
56        # Define some constants
57        self.colorlist = ['b','g','r','c','m','y']
58        self.symbollist = ['o','x','^','v','<','>','+','s','d','D','h','H','p']
59        #User scale
60        self.xscales =""
61        self.yscales =""
62        # keep track if the previous transformation of x and y in Property dialog
63        self.prevXtrans =" "
64        self.prevYtrans =" "
65       
66    def setTrans(self,xtrans,ytrans): 
67        """
68            @param xtrans: set x transformation on Property dialog
69            @param ytrans: set y transformation on Property dialog
70        """
71        self.prevXtrans =xtrans
72        self.prevYtrans =ytrans
73       
74    def onFitting(self, event): 
75        """
76            when clicking on linear Fit on context menu , display Fitting Dialog
77        """
78        list =[]
79        list = self.graph.returnPlottable()
80        from fitDialog import LinearFit
81        print len(list)
82        if len(list.keys())>0:
83            first_item = list.keys()[0]
84            print first_item, list[first_item].__class__.__name__
85            dlg = LinearFit( None, first_item, self.onFitDisplay, -1, 'Fitting')
86            dlg.ShowModal() 
87
88    def _onProperties(self, event):
89        """
90            when clicking on Properties on context menu ,The Property dialog is displayed
91            The user selects a transformation for x or y value and a new plot is displayed
92        """
93        from PropertyDialog import Properties
94        dial = Properties(self, -1, 'Properties')
95        dial.setValues( self.prevXtrans, self.prevYtrans )
96        if dial.ShowModal() == wx.ID_OK:
97            self.xscales, self.yscales = dial.getValues()
98            self._onEVT_FUNC_PROPERTY()
99        dial.Destroy()
100           
101    def toX(self,x):
102        """
103            This function is used to load value on Plottable.View
104            @param x: Float value
105            @return x,
106        """
107        return x
108   
109    def toX2(self,x):
110        """
111            This function is used to load value on Plottable.View
112            Calculate x^(2)
113            @param x: float value
114        """
115        return math.pow(x,2)
116   
117    def fromX2(self,x):
118         """
119             This function is used to load value on Plottable.View
120            Calculate square root of x
121            @param x: float value
122         """
123         if not x >=0 :
124             raise ValueError, "square root of a negative value "
125         else:
126             return math.sqrt(x)
127         
128    def toLogXY(self,x,y):
129        """
130            This function is used to load value on Plottable.View
131            calculate log x
132            @param x: float value
133        """
134        if not x*y > 0:
135            raise ValueError, "Log(X*Y)of a negative value "
136        else:
137            return math.log(x*y)
138       
139    def fromLogXY(self,x):
140        """
141            This function is used to load value on Plottable.View
142            Calculate e^(x)
143            @param x: float value
144        """
145        return math.exp(x*y)
146   
147    def set_yscale(self, scale='linear'):
148        """
149            Set the scale on Y-axis
150            @param scale: the scale of y-axis
151        """
152        self.subplot.set_yscale(scale)
153        self.yscale = scale
154       
155    def get_yscale(self):
156        """
157             @return: Y-axis scale
158        """
159        return self.yscale
160   
161    def set_xscale(self, scale='linear'):
162        """
163            Set the scale on x-axis
164            @param scale: the scale of x-axis
165        """
166        self.subplot.set_xscale(scale)
167        self.xscale = scale
168       
169    def get_xscale(self):
170        """
171             @return: x-axis scale
172        """
173        return self.xscale
174
175    def SetColor(self, rgbtuple):
176        """Set figure and canvas colours to be the same"""
177        if not rgbtuple:
178            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
179        col = [c/255.0 for c in rgbtuple]
180        self.figure.set_facecolor(col)
181        self.figure.set_edgecolor(col)
182        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
183
184    def _onSize(self, event):
185        self._resizeflag = True
186
187    def _onIdle(self, evt):
188        if self._resizeflag:
189            self._resizeflag = False
190            self._SetSize()
191            self.draw()
192
193    def _SetSize(self, pixels = None):
194        """
195        This method can be called to force the Plot to be a desired size, which defaults to
196        the ClientSize of the panel
197        """
198        if not pixels:
199            pixels = self.GetClientSize()
200        self.canvas.SetSize(pixels)
201        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
202        pixels[1]/self.figure.get_dpi())
203
204    def draw(self):
205        """Where the actual drawing happens"""
206        self.figure.canvas.draw_idle()
207       
208
209
210    def onSaveImage(self, evt):
211        #figure.savefig
212        print "Save image not implemented"
213        path = None
214        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.SAVE)
215        if dlg.ShowModal() == wx.ID_OK:
216            path = dlg.GetPath()
217            mypath = os.path.basename(path)
218            print path
219        dlg.Destroy()
220        if not path == None:
221            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
222                                        orentation='portrait', papertype=None, format='png')
223       
224    def onContextMenu(self, event):
225        """
226            Default context menu for a plot panel
227        """
228        # Slicer plot popup menu
229        slicerpop = wx.Menu()
230        slicerpop.Append(313,'&Save image', 'Save image as PNG')
231        wx.EVT_MENU(self, 313, self.onSaveImage)
232        slicerpop.Append(316, '&Load 1D data file')
233       
234        wx.EVT_MENU(self, 314, self.onSave1DData)
235        wx.EVT_MENU(self, 316, self._onLoad1DData)
236        slicerpop.AppendSeparator()
237        slicerpop.Append(315, '&Properties')
238       
239        slicerpop.AppendSeparator()
240        slicerpop.Append(317, '&Linear Fit')
241       
242        wx.EVT_MENU(self, 314, self.onSave1DData)
243        wx.EVT_MENU(self, 316, self._onLoad1DData)
244        wx.EVT_MENU(self, 315, self._onProperties)
245        wx.EVT_MENU(self, 317, self.onFitting)
246       
247        pos = event.GetPosition()
248        pos = self.ScreenToClient(pos)
249        self.PopupMenu(slicerpop, pos)
250       
251   
252    ## The following is plottable functionality
253
254
255    def properties(self,prop):
256        """Set some properties of the graph.
257       
258        The set of properties is not yet determined.
259        """
260        # The particulars of how they are stored and manipulated (e.g., do
261        # we want an inventory internally) is not settled.  I've used a
262        # property dictionary for now.
263        #
264        # How these properties interact with a user defined style file is
265        # even less clear.
266
267        # Properties defined by plot
268        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
269        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
270        self.subplot.set_title(prop["title"])
271
272        # Properties defined by user
273        #self.axes.grid(True)
274
275    def clear(self):
276        """Reset the plot"""
277       
278        # TODO: Redraw is brutal.  Render to a backing store and swap in
279        # TODO: rather than redrawing on the fly.
280        self.subplot.clear()
281        self.subplot.hold(True)
282
283
284    def render(self):
285        """Commit the plot after all objects are drawn"""
286        # TODO: this is when the backing store should be swapped in.
287        from matplotlib.font_manager import FontProperties
288        self.subplot.legend(prop=FontProperties(size=10))
289        #self.subplot.legend()
290        pass
291
292    def xaxis(self,label,units):
293        """xaxis label and units.
294       
295        Axis labels know about units.
296       
297        We need to do this so that we can detect when axes are not
298        commesurate.  Currently this is ignored other than for formatting
299        purposes.
300        """
301        if units != "": label = label + " (" + units + ")"
302        self.subplot.set_xlabel(label)
303        pass
304   
305    def yaxis(self,label,units):
306        """yaxis label and units."""
307        if units != "": label = label + " (" + units + ")"
308        self.subplot.set_ylabel(label)
309        pass
310
311    def _connect_to_xlim(self,callback):
312        """Bind the xlim change notification to the callback"""
313        def process_xlim(axes):
314            lo,hi = subplot.get_xlim()
315            callback(lo,hi)
316        self.subplot.callbacks.connect('xlim_changed',process_xlim)
317   
318    #def connect(self,trigger,callback):
319    #    print "PlotPanel.connect???"
320    #    if trigger == 'xlim': self._connect_to_xlim(callback)
321
322    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
323        """Draw markers with error bars"""
324        self.subplot.set_yscale('linear')
325        self.subplot.set_xscale('linear')
326        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
327        if dx != None and type(dx) == type(()):
328            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
329        if dy != None and type(dy) == type(()):
330            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
331
332        if dx==None and dy==None:
333            h = self.subplot.plot(x,y,color=self._color(color),
334                                   marker=self._symbol(symbol),linestyle='',label=label)
335        else:
336            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
337             ecolor=self._color(color), capsize=2,linestyle='', barsabove=False,
338             marker=self._symbol(symbol),
339             lolims=False, uplims=False,
340             xlolims=False, xuplims=False,label=label)
341           
342        self.subplot.set_yscale(self.yscale)
343        self.subplot.set_xscale(self.xscale)
344
345    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
346        """Draw a line on a graph, possibly with confidence intervals."""
347        c = self._color(color)
348        self.subplot.set_yscale('linear')
349        self.subplot.set_xscale('linear')
350       
351        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
352       
353        self.subplot.set_yscale(self.yscale)
354        self.subplot.set_xscale(self.xscale)
355
356    def _color(self,c):
357        """Return a particular colour"""
358        return self.colorlist[c%len(self.colorlist)]
359
360    def _symbol(self,s):
361        """Return a particular symbol"""
362        return self.symbollist[s%len(self.symbollist)]
363   
364    def _onEVT_FUNC_PROPERTY(self):
365        """
366             Receive the scale from myDialog and set the scale
367        """ 
368        list =[]
369        list = self.graph.returnPlottable()
370        for item in list:
371            if ( self.xscales=="x" ):
372                if self.prevXtrans == "x^(2)":
373                    item.transform_x(  self.fromX2, self.errFuncfromX2 )
374                #elif self.prevXtrans == "Log(x)"
375                    #item.transform_x(  self.fromLogXY,self.errFuncfromX2 )
376                print "Values of  x",item.x[0:5]
377                print "Values of view x",item.view.x[0:5]
378                self.set_xscale("linear")
379                self.graph.xaxis('\\rm{q} ', 'A^{-1}')
380               
381            if ( self.xscales=="x^(2)" ):
382                #if self.prevXtrans == "Log(x)":
383                    #item.transform_x(  self.fromLogXY, self.errFunc )
384                if  self.prevXtrans != "x^(2)":
385                    item.transform_x(  self.toX2, self.errFuncToX2 )
386                    print "Values of  x",item.x[0:5]
387                    print "Values of view x^(2)",item.view.x[0:5]
388                self.set_xscale('linear')
389                self.graph.xaxis('\\rm{q^{2}} ', 'A^{-2}')
390               
391            if (self.xscales=="Log(x)" ):
392                if self.prevXtrans == "x^(2)":
393                    item.transform_x(  self.fromX2, self.errFuncfromX2 )
394                #elif self.prevXtrans == "Log(x)":
395                    #item.transform_x(  self.toLogXY, self.errFunc )
396                #self.set_xscale("linear")
397                self.set_xscale('log')
398                self.graph.xaxis('\\rm{log(q)} ', 'A^{-1}')
399               
400            if ( self.yscales=="y" ):
401                if self.prevYtrans == "y^(2)":
402                    item.transform_y(  self.toX2, self.errFuncToX2 )
403                #elif self.prevXtrans == "Log(y)"
404                    #item.transform_y(  self.fromLogXY.errFunc )   
405                self.set_yscale("linear")
406                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
407               
408            if ( self.yscales=="Log(y)" ):
409                if self.prevYtrans == "y^(2)":
410                     item.transform_y(  self.fromX2, self.errFuncfromX2 )
411                #elif self.prevYtrans != "Log(y)":
412                    #item.transform_y(  self.toLogXY, self.errFunc )
413                #self.set_yscale("linear") 
414                self.set_yscale("log") 
415                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
416               
417            if ( self.yscales=="y^(2)" ):
418                 #if self.prevYtrans == "Log(y)":
419                       #item.transform_y(  self.fromLogXY, self.errFunc )
420                if self.prevYtrans != "y^(2)":
421                     item.transform_y(  self.toX2, self.errFuncToX2 )   
422                self.set_yscale("linear")
423                self.graph.yaxis("\\rm{Intensity^{2}} ","cm^{-2}")
424            item.set_View(item.x,item.y) 
425             
426        self.prevXtrans = self.xscales
427        self.prevYtrans = self.yscales 
428       
429        self.graph.render(self)
430        self.subplot.figure.canvas.draw_idle()
431       
432    def errFuncToX2(self,x,dx=None):
433        """
434            calculate error of x**2
435            @param x: float value
436            @param dx: float value
437        """
438        if not dx == None:
439            return 2*x*dx
440        else:
441            raise ValueError, "Can't  calculate the error"
442    def errFuncfromX2(self,x,dx=None):
443        """
444            calculate error of sqrt(x)
445            @param x: float value
446            @param dx: float value
447        """
448        if (x > 0) and (dx != None):
449            err= dx/2*math.sqrt(x)
450           
451        else:
452            raise ValueError, "Can't  calculate the error"
453    def errFuncToLogX(self,x,dx=None):
454        """
455            calculate error of Log(xy)
456            @param x: float value
457            @param dx: float value
458        """
459        if (x!=0) and ( dx != None):
460            err= dx/x
461            if err >= x:
462                err = 0.9*x
463            return err
464        elif x==0:
465            raise ValueError, "On calculate error: Can't divide by zero"
466        else:
467            raise ValueError, "Can't  calculate the error"
468       
469    def errFuncfromLogXY(self,x,dx=None):
470        """
471            calculate error of sqrt(x)
472            @param x: float value
473            @param dx: float value
474        """
475        if (x > 0) and (dx != None):
476            err= dx/2*math.sqrt(x)
477            if err >= x:
478                err = 0.9*x
479            return err
480        else:
481            raise ValueError, "Can't  calculate the error"
482                   
483    def onFitDisplay(self, plottable):
484        """
485            Add a new plottable into the graph .In this case this plottable will be used
486            to fit some data
487            @param plottable: the plottable to plot
488        """
489        self.graph.add(plottable)
490        self.graph.render(self)
491        self.subplot.figure.canvas.draw_idle()
492     
493   
494class NoRepaintCanvas(FigureCanvasWxAgg):
495    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
496    the draw method is only called for the first two paint events. After that,
497    the canvas will only be redrawn when it is resized.
498    """
499    def __init__(self, *args, **kwargs):
500        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
501        self._drawn = 0
502
503    def _onPaint(self, evt):
504        """
505        Called when wxPaintEvt is generated
506        """
507        if not self._isRealized:
508            self.realize()
509        if self._drawn < 2:
510            self.draw(repaint = False)
511            self._drawn += 1
512        self.gui_repaint(drawDC=wx.PaintDC(self))
513           
Note: See TracBrowser for help on using the repository browser.