source: sasview/guitools/PlotPanel.py @ 05da1f89

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 05da1f89 was 05da1f89, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Some mods to improve the look of the fit dialog and fix minor bugs.

  • Property mode set to 100644
File size: 23.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
10import transform
11from canvas import FigureCanvas
12from matplotlib.widgets import RectangleSelector
13from pylab import  gca, gcf
14from plottables import Theory1D
15#from plottables import Data1D
16#TODO: make the plottables interactive
17
18DEBUG = False
19
20from plottables import Graph
21#(FuncFitEvent, EVT_FUNC_FIT) = wx.lib.newevent.NewEvent()
22import math,pylab
23def show_tree(obj,d=0):
24    """Handy function for displaying a tree of graph objects"""
25    print "%s%s" % ("-"*d,obj.__class__.__name__)
26    if 'get_children' in dir(obj):
27        for a in obj.get_children(): show_tree(a,d+1)
28def _rescale(lo,hi,step,pt=None,bal=None,scale='linear'):
29        """
30        Rescale (lo,hi) by step, returning the new (lo,hi)
31        The scaling is centered on pt, with positive values of step
32        driving lo/hi away from pt and negative values pulling them in.
33        If bal is given instead of point, it is already in [0,1] coordinates.
34   
35        This is a helper function for step-based zooming.
36        """
37        # Convert values into the correct scale for a linear transformation
38        # TODO: use proper scale transformers
39        loprev = lo
40        hiprev = hi
41        ptprev = pt
42        if scale=='log':
43            assert lo >0
44            if lo > 0 :
45                lo = math.log10(lo)
46            if hi > 0 :
47                hi = math.log10(hi)
48            if pt is not None: pt = math.log10(pt)
49       
50        # Compute delta from axis range * %, or 1-% if persent is negative
51        if step > 0:
52            delta = float(hi-lo)*step/100
53        else:
54            delta = float(hi-lo)*step/(100-step)
55   
56        # Add scale factor proportionally to the lo and hi values, preserving the
57        # point under the mouse
58        if bal is None:
59            bal = float(pt-lo)/(hi-lo)
60        lo = lo - bal*delta
61        hi = hi + (1-bal)*delta
62   
63        # Convert transformed values back to the original scale
64        if scale=='log':
65            if (lo <= -250) or (hi >= 250):
66                lo=loprev
67                hi=hiprev
68                print "Not possible to scale"
69           
70            else:
71                lo,hi = math.pow(10.,lo),math.pow(10.,hi)
72                #assert lo >0,"lo = %g"%lo
73                print "possible to scale"
74           
75            print "these are low and high",lo,hi
76
77        return (lo,hi)
78
79
80class PlotPanel(wx.Panel):
81    """
82    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
83    flag, and the actually redrawing of the
84    figure is triggered by an Idle event.
85    """
86    def __init__(self, parent, id = -1, color = None,\
87        dpi = None, **kwargs):
88        wx.Panel.__init__(self, parent, id = id, **kwargs)
89        self.parent = parent
90        self.figure = Figure(None, dpi)
91        #self.figure = pylab.Figure(None, dpi)
92        #self.canvas = NoRepaintCanvas(self, -1, self.figure)
93        self.canvas = FigureCanvas(self, -1, self.figure)
94        self.SetColor(color)
95        #self.Bind(wx.EVT_IDLE, self._onIdle)
96        #self.Bind(wx.EVT_SIZE, self._onSize)
97        self._resizeflag = True
98        self._SetSize()
99        self.subplot = self.figure.add_subplot(111)
100        self.figure.subplots_adjust(left=.2, bottom=.2)
101        self.yscale = 'linear'
102        self.xscale = 'linear'
103        sizer = wx.BoxSizer(wx.VERTICAL)
104        sizer.Add(self.canvas,1,wx.EXPAND)
105        self.SetSizer(sizer)
106       
107        # Graph object to manage the plottables
108        self.graph = Graph()
109        #self.Bind(EVT_FUNC_FIT, self.onFitRange)
110        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
111        #self.Bind(EVT_PROPERTY, self._onEVT_FUNC_PROPERTY)
112        # Define some constants
113        self.colorlist = ['b','g','r','c','m','y']
114        self.symbollist = ['o','x','^','v','<','>','+','s','d','D','h','H','p']
115        #User scale
116        self.xLabel ="x"
117        self.yLabel ="log10(y)"
118        self.viewModel ="--"
119        # keep track if the previous transformation of x and y in Property dialog
120        self.prevXtrans =" "
121        self.prevYtrans =" "
122        self.canvas.mpl_connect('scroll_event',self.onWheel)
123        self.axes = [self.subplot]
124         # new data for the fit
125        self.fit_result = Theory1D(x=[], y=[], dy=None)
126        #self.fit_result = Data1D(x=[], y=[],dx=None, dy=None)
127        self.fit_result.name = "Fit"
128        # For fit Dialog initial display
129        self.xmin=0.0
130        self.xmax=0.0
131        self.xminView=0.0
132        self.xmaxView=0.0
133        self.Avalue=None
134        self.Bvalue=None
135        self.ErrAvalue=None
136        self.ErrBvalue=None
137        self.Chivalue=None
138    def resetFitView(self):
139        """
140             For fit Dialog initial display
141        """
142        self.xmin=0.0
143        self.xmax=0.0
144        self.xminView=0.0
145        self.xmaxView=0.0
146        self.Avalue=None
147        self.Bvalue=None
148        self.ErrAvalue=None
149        self.ErrBvalue=None
150        self.Chivalue=None
151       
152    def onWheel(self, event):
153        """
154            Process mouse wheel as zoom events
155            @param event: Wheel event
156        """
157        ax = event.inaxes
158        step = event.step
159
160        if ax != None:
161            # Event occurred inside a plotting area
162            lo,hi = ax.get_xlim()
163            lo,hi = _rescale(lo,hi,step,pt=event.xdata,scale=ax.get_xscale())
164            if not self.xscale=='log' or lo>0:
165                ax.set_xlim((lo,hi))
166
167            lo,hi = ax.get_ylim()
168            lo,hi = _rescale(lo,hi,step,pt=event.ydata,scale=ax.get_yscale())
169            if not self.yscale=='log' or lo>0:
170                ax.set_ylim((lo,hi))
171        else:
172             # Check if zoom happens in the axes
173            xdata,ydata = None,None
174            x,y = event.x,event.y
175           
176            for ax in self.axes:
177                insidex,_ = ax.xaxis.contains(event)
178                if insidex:
179                    xdata,_ = ax.transAxes.inverse_xy_tup((x,y))
180                insidey,_ = ax.yaxis.contains(event)
181                if insidey:
182                    _,ydata = ax.transAxes.inverse_xy_tup((x,y))
183            if xdata is not None:
184                lo,hi = ax.get_xlim()
185                lo,hi = _rescale(lo,hi,step,bal=xdata,scale=ax.get_xscale())
186                if not self.xscale=='log' or lo>0:
187                    ax.set_xlim((lo,hi))
188            if ydata is not None:
189                lo,hi = ax.get_ylim()
190                lo,hi = _rescale(lo,hi,step,bal=ydata,scale=ax.get_yscale())
191                if not self.yscale=='log' or lo>0:
192                    ax.set_ylim((lo,hi))
193               
194        self.canvas.draw_idle()
195
196
197    def returnTrans(self):
198        """
199            Return values and labels used by Fit Dialog
200        """
201        return self.xLabel,self.yLabel, self.Avalue, self.Bvalue,\
202                self.ErrAvalue,self.ErrBvalue,self.Chivalue
203   
204    def setTrans(self,xtrans,ytrans): 
205        """
206            @param xtrans: set x transformation on Property dialog
207            @param ytrans: set y transformation on Property dialog
208        """
209        self.prevXtrans =xtrans
210        self.prevYtrans =ytrans
211   
212    def onFitting(self, event): 
213        """
214            when clicking on linear Fit on context menu , display Fitting Dialog
215        """
216        list =[]
217        list = self.graph.returnPlottable()
218        from fitDialog import LinearFit
219       
220        if len(list.keys())>0:
221            first_item = list.keys()[0]
222            dlg = LinearFit( None, first_item, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
223           
224            if (self.xmin !=0.0 )and ( self.xmax !=0.0)\
225                and(self.xminView !=0.0 )and ( self.xmaxView !=0.0):
226                dlg.setFitRange(self.xminView,self.xmaxView,self.xmin,self.xmax)
227            dlg.ShowModal() 
228
229    def _onProperties(self, event):
230        """
231            when clicking on Properties on context menu ,The Property dialog is displayed
232            The user selects a transformation for x or y value and a new plot is displayed
233        """
234        list =[]
235        list = self.graph.returnPlottable()
236        if len(list.keys())>0:
237            first_item = list.keys()[0]
238            if first_item.x !=[]:
239                from PropertyDialog import Properties
240                dial = Properties(self, -1, 'Properties')
241                dial.setValues( self.prevXtrans, self.prevYtrans,self.viewModel )
242                if dial.ShowModal() == wx.ID_OK:
243                    self.xLabel, self.yLabel,self.viewModel = dial.getValues()
244                    if self.viewModel =="Guinier lny vs x^(2)":
245                        self.xLabel="x^(2)"
246                        self.yLabel="ln(y)"
247                        self.viewModel = "--"
248                        dial.setValues( self.xLabel, self.yLabel,self.viewModel )
249                    self._onEVT_FUNC_PROPERTY()
250                dial.Destroy()
251           
252 
253    def set_yscale(self, scale='linear'):
254        """
255            Set the scale on Y-axis
256            @param scale: the scale of y-axis
257        """
258        self.subplot.set_yscale(scale)
259        self.yscale = scale
260       
261    def get_yscale(self):
262        """
263             @return: Y-axis scale
264        """
265        return self.yscale
266   
267    def set_xscale(self, scale='linear'):
268        """
269            Set the scale on x-axis
270            @param scale: the scale of x-axis
271        """
272        self.subplot.set_xscale(scale)
273        self.xscale = scale
274       
275    def get_xscale(self):
276        """
277             @return: x-axis scale
278        """
279        return self.xscale
280
281    def SetColor(self, rgbtuple):
282        """Set figure and canvas colours to be the same"""
283        if not rgbtuple:
284            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
285        col = [c/255.0 for c in rgbtuple]
286        self.figure.set_facecolor(col)
287        self.figure.set_edgecolor(col)
288        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
289
290    def _onSize(self, event):
291        self._resizeflag = True
292
293    def _onIdle(self, evt):
294        if self._resizeflag:
295            self._resizeflag = False
296            self._SetSize()
297            self.draw()
298
299    def _SetSize(self, pixels = None):
300        """
301        This method can be called to force the Plot to be a desired size, which defaults to
302        the ClientSize of the panel
303        """
304        if not pixels:
305            pixels = self.GetClientSize()
306        self.canvas.SetSize(pixels)
307        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
308        pixels[1]/self.figure.get_dpi())
309
310    def draw(self):
311        """Where the actual drawing happens"""
312        self.figure.canvas.draw_idle()
313       
314
315 
316 
317       
318    def onSaveImage(self, evt):
319        #figure.savefig
320        #print "Save image not implemented"
321        path = None
322        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.SAVE)
323        if dlg.ShowModal() == wx.ID_OK:
324            path = dlg.GetPath()
325            mypath = os.path.basename(path)
326            print path
327        dlg.Destroy()
328        if not path == None:
329            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
330                                        orentation='portrait', papertype=None, format='png')
331       
332    def onContextMenu(self, event):
333        """
334            Default context menu for a plot panel
335        """
336        # Slicer plot popup menu
337        slicerpop = wx.Menu()
338        slicerpop.Append(313,'&Save image', 'Save image as PNG')
339        wx.EVT_MENU(self, 313, self.onSaveImage)
340       
341        slicerpop.Append(316, '&Load 1D data file')
342        wx.EVT_MENU(self, 316, self._onLoad1DData)
343       
344        slicerpop.AppendSeparator()
345        slicerpop.Append(315, '&Properties')
346        wx.EVT_MENU(self, 315, self._onProperties)
347       
348        slicerpop.AppendSeparator()
349        slicerpop.Append(317, '&Linear Fit')
350        wx.EVT_MENU(self, 317, self.onFitting)
351       
352        slicerpop.AppendSeparator()
353        slicerpop.Append(318, '&Reset Graph')
354        wx.EVT_MENU(self, 318, self.onResetGraph)
355       
356        pos = event.GetPosition()
357        pos = self.ScreenToClient(pos)
358        self.PopupMenu(slicerpop, pos)
359   
360    ## The following is plottable functionality
361
362
363    def properties(self,prop):
364        """Set some properties of the graph.
365       
366        The set of properties is not yet determined.
367        """
368        # The particulars of how they are stored and manipulated (e.g., do
369        # we want an inventory internally) is not settled.  I've used a
370        # property dictionary for now.
371        #
372        # How these properties interact with a user defined style file is
373        # even less clear.
374
375        # Properties defined by plot
376        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
377        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
378        self.subplot.set_title(prop["title"])
379
380        # Properties defined by user
381        #self.axes.grid(True)
382
383    def clear(self):
384        """Reset the plot"""
385       
386        # TODO: Redraw is brutal.  Render to a backing store and swap in
387        # TODO: rather than redrawing on the fly.
388        self.subplot.clear()
389        self.subplot.hold(True)
390   
391    def render(self):
392        """Commit the plot after all objects are drawn"""
393        # TODO: this is when the backing store should be swapped in.
394        from matplotlib.font_manager import FontProperties
395        self.subplot.legend(prop=FontProperties(size=10))
396        #self.subplot.legend()
397        pass
398
399    def xaxis(self,label,units):
400        """xaxis label and units.
401       
402        Axis labels know about units.
403       
404        We need to do this so that we can detect when axes are not
405        commesurate.  Currently this is ignored other than for formatting
406        purposes.
407        """
408        if units != "": label = label + " (" + units + ")"
409        self.subplot.set_xlabel(label)
410        pass
411   
412    def yaxis(self,label,units):
413        """yaxis label and units."""
414        if units != "": label = label + " (" + units + ")"
415        self.subplot.set_ylabel(label)
416        pass
417
418    def _connect_to_xlim(self,callback):
419        """Bind the xlim change notification to the callback"""
420        def process_xlim(axes):
421            lo,hi = subplot.get_xlim()
422            callback(lo,hi)
423        self.subplot.callbacks.connect('xlim_changed',process_xlim)
424   
425    #def connect(self,trigger,callback):
426    #    print "PlotPanel.connect???"
427    #    if trigger == 'xlim': self._connect_to_xlim(callback)
428
429    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
430        """Draw markers with error bars"""
431        self.subplot.set_yscale('linear')
432        self.subplot.set_xscale('linear')
433        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
434        if dx != None and type(dx) == type(()):
435            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
436        if dy != None and type(dy) == type(()):
437            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
438
439        if dx==None and dy==None:
440            h = self.subplot.plot(x,y,color=self._color(color),
441                                   marker=self._symbol(symbol),linestyle='',label=label)
442        else:
443            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
444             ecolor=self._color(color), capsize=2,linestyle='', barsabove=False,
445             marker=self._symbol(symbol),
446             lolims=False, uplims=False,
447             xlolims=False, xuplims=False,label=label)
448           
449        self.subplot.set_yscale(self.yscale)
450        self.subplot.set_xscale(self.xscale)
451
452    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
453        """Draw a line on a graph, possibly with confidence intervals."""
454        c = self._color(color)
455        self.subplot.set_yscale('linear')
456        self.subplot.set_xscale('linear')
457       
458        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
459       
460        self.subplot.set_yscale(self.yscale)
461        self.subplot.set_xscale(self.xscale)
462
463    def _color(self,c):
464        """Return a particular colour"""
465        return self.colorlist[c%len(self.colorlist)]
466
467    def _symbol(self,s):
468        """Return a particular symbol"""
469        return self.symbollist[s%len(self.symbollist)]
470   
471    def _onEVT_FUNC_PROPERTY(self):
472        """
473             Receive the x and y transformation from myDialog,Transforms x and y in View
474              and set the scale   
475        """ 
476        list =[]
477        list = self.graph.returnPlottable()
478        self.fit_result.x =[] 
479        self.fit_result.y =[] 
480        self.fit_result.dx=None
481        self.fit_result.dy=None
482       
483        for item in list:
484            item.setLabel(self.xLabel,self.yLabel)
485            if ( self.xLabel=="x" ):
486                item.transformX(transform.toX,transform.errToX)
487                self.set_xscale("linear")
488                name, units = item.get_xaxis()
489                self.graph.xaxis("%s" % name,  "%s^{-1}" % units)
490               
491            if ( self.xLabel=="x^(2)" ):
492                item.transformX(transform.toX2,transform.errToX2)
493                self.set_xscale('linear')
494                name, units = item.get_xaxis()
495                self.graph.xaxis("%s^{2}" % name,  "%s^{-2}" % units)
496               
497            if (self.xLabel=="log10(x)" ):
498                item.transformX(transform.toX,transform.errToX)
499                self.set_xscale("log")
500                name, units = item.get_xaxis() 
501                self.graph.xaxis("\log_{10}\ \  (%s)" % name,  "%s^{-1}" % units)
502               
503            if ( self.yLabel=="ln(y)" ):
504                item.transformY(transform.toLogX,transform.errToLogX)
505                self.set_yscale("linear")
506                name, units = item.get_yaxis()
507                self.graph.yaxis("log\ \ %s" % name,  "%s^{-1}" % units)
508               
509            if ( self.yLabel=="y" ):
510                item.transformY(transform.toX,transform.errToX)
511                self.set_yscale("linear")
512                name, units = item.get_yaxis()
513                self.graph.yaxis("%s" % name,  "%s^{-1}" % units)
514               
515            if ( self.yLabel=="log10(y)" ): 
516                item.transformY(transform.toX,transform.errToX)
517                self.set_yscale("log") 
518                name, units = item.get_yaxis()
519                self.graph.yaxis("\log_{10}\ \ (%s)" % name,  "%s^{-1}" % units)
520               
521            if ( self.yLabel=="y^(2)" ):
522                item.transformY( transform.toX2,transform.errToX2 )   
523                self.set_yscale("linear")
524                name, units = item.get_yaxis()
525                self.graph.yaxis("%s^{2}" % name,  "%s^{-2}" % units)
526               
527            if ( self.yLabel =="1/y"):
528                item.transformY(transform.toOneOverX,transform.errOneOverX )
529                self.set_yscale("linear")
530                name, units = item.get_yaxis()
531                self.graph.yaxis("1/%s" % name,  "\ \%s" % units)
532               
533            if ( self.yLabel =="1/sqrt(y)" ):
534                item.transformY(transform.toOneOverSqrtX,transform.errOneOverSqrtX )
535                self.set_yscale("linear")
536                name, units = item.get_yaxis()
537                self.graph.yaxis("1/\sqrt{%s}" %name,  "%s" % units)
538               
539            if ( self.yLabel =="ln(y*x)"):
540                item.transformY( transform.toLogXY,transform.errToLogXY)
541                self.set_yscale("linear")
542                yname, yunits = item.get_yaxis()
543                xname, xunits = item.get_xaxis()
544                self.graph.yaxis("log\ (%s \ \ %s)" % (yname,xname),  "%s^{-1}%s^{-1}" % (yunits,xunits))
545               
546            if ( self.yLabel =="ln(y*x^(2))"):
547                item.transformY( transform.toLogYX2,transform.errToLogYX2)
548                self.set_yscale("linear")
549                yname, yunits = item.get_yaxis()
550                xname, xunits = item.get_xaxis() 
551                self.graph.yaxis("Log (%s \ \ %s^{2})" % (yname,xname),  "%s^{-1}%s^{-2}" % (yunits,xunits))
552           
553            if ( self.yLabel =="ln(y*x^(4))"):
554                item.transformY(transform.toLogYX4,transform.errToLogYX4)
555                self.set_yscale("linear")
556                yname, yunits = item.get_yaxis()
557                xname, xunits = item.get_xaxis()
558                self.graph.yaxis("Log (%s \ \ %s^{4})" % (yname,xname),  "%s^{-1}%s^{-4}" % (yunits,xunits))
559           
560            if ( self.viewModel == "Guinier lny vs x^(2)"):
561               
562                item.transformX(transform.toX2,transform.errToX2)
563                self.set_xscale('linear')
564                name, units = item.get_xaxis()
565                self.graph.xaxis("%s^{2}" % name,  "%s^{-2}" % units)
566               
567                item.transformY(transform.toLogX,transform.errToLogX )
568                self.set_yscale("linear")
569                name, units = item.get_yaxis()
570                self.graph.yaxis("$Log %s$" % name,  "%s^{-1}" % units)
571               
572            item.transformView()
573           
574        #item.name = self.yLabel+" vs " +self.xLabel 
575        self.resetFitView()   
576        self.prevXtrans = self.xLabel
577        self.prevYtrans = self.yLabel 
578        self.graph.render(self)
579        self.subplot.figure.canvas.draw_idle()
580       
581    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
582        """
583            Add a new plottable into the graph .In this case this plottable will be used
584            to fit some data
585            @param tempx: The x data of fit line
586            @param tempy: The y data of fit line
587            @param xminView: the lower bound of fitting range
588            @param xminView: the upper bound of  fitting range
589            @param xmin: the lowest value of data to fit to the line
590            @param xmax: the highest value of data to fit to the line
591        """
592        # Saving value to redisplay in Fit Dialog when it is opened again
593        self.Avalue,self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue=func
594        self.xminView=xminView
595        self.xmaxView=xmaxView
596        self.xmin= xmin
597        self.xmax= xmax
598        #In case need to change the range of data plotted
599        list =[]
600        list = self.graph.returnPlottable()
601        for item in list:
602            #item.onFitRange(xminView,xmaxView)
603            item.onFitRange(None,None)
604       
605        # Create new data plottable with result
606        self.fit_result.x =[] 
607        self.fit_result.y =[]
608        self.fit_result.x =tempx 
609        self.fit_result.y =tempy     
610        self.fit_result.dx=None
611        self.fit_result.dy=None
612        #Load the view with the new values
613        self.fit_result.reset_view()
614        # Add the new plottable to the graph
615        self.graph.add(self.fit_result) 
616        self.graph.render(self)
617        self.subplot.figure.canvas.draw_idle()
618       
619   
620    def onResetGraph(self,event):
621        """
622            Reset the graph by plotting the full range of data
623        """
624        list =[]
625        list = self.graph.returnPlottable()
626        for item in list:
627            item.onReset()
628        self.graph.render(self)
629        self.subplot.figure.canvas.draw_idle()
630       
631class NoRepaintCanvas(FigureCanvasWxAgg):
632    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
633    the draw method is only called for the first two paint events. After that,
634    the canvas will only be redrawn when it is resized.
635    """
636    def __init__(self, *args, **kwargs):
637        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
638        self._drawn = 0
639
640    def _onPaint(self, evt):
641        """
642        Called when wxPaintEvt is generated
643        """
644        if not self._isRealized:
645            self.realize()
646        if self._drawn < 2:
647            self.draw(repaint = False)
648            self._drawn += 1
649        self.gui_repaint(drawDC=wx.PaintDC(self))
650           
Note: See TracBrowser for help on using the repository browser.