source: sasview/guitools/PlotPanel.py @ fa452e4

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 fa452e4 was 34ab06d, checked in by Gervaise Alina <gervyh@…>, 17 years ago

changed on plotpanel

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