I continued to work on this issue. Thanks for Chris Barker for pointing 
me into the right direction. I also had a look at other gui backends, 
and, among other, backend_qtagg.py seems to contain a proper (more or 
less, see other postings of Ted Drain) implementation of double buffered 
drawing that avoids unnecessary rerendering of the bitmap. Following 
this example, I applied following changes to backend_wx.py and 
backend_wxagg.py

backend_wx.py:

in __init__(...) added line:

self._need_rerender = True

changed _onPaint(...) to following (note: removed evt.Skip() at end!)

    def _onPaint(self, evt):
        """
        Called when wxPaintEvt is generated
        """
        DEBUG_MSG("_onPaint()", 1, self)
        if not self._isRealized:
            self.realize()
        #only recreate bitmap if needed
        if self._need_rerender:
            self.draw(repaint=False)
            self._need_rerender = False
        #repaint only damaged parts of window
        dc = wx.PaintDC(self)
        source = wx.MemoryDC(self.bitmap)
        box = self.UpdateRegion.Box
        dc.Blit(box.X, box.Y, box.Width, box.Height,
                source,
                box.X, box.Y)
        source.SelectObject(wx.NullBitmap) #needed?

By these change in onPaint a rerendering of the bitmap is done only if
needed (in fact, this is needed only once after the figure is shown
for the first time). I moved code from gui_repaint() into
_onPaint. Calls to gui_repaint() in other methods (e.g., draw) might now be
replaced by

self.Refresh()
self.Update() #this is optional, leeds to an immediate repaint

in backend_wxagg.py I changed the draw and blit methods in this sense:

    def draw(self, repaint=True):
        """
        Render the figure using agg.
        """
        DEBUG_MSG("draw()", 1, self)
       
        FigureCanvasAgg.draw(self)
        self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
        if repaint:
            self.Refresh(eraseBackground = False)
            self.Update()
    def blit(self, bbox=None):
        self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
       
        if bbox is None:
            self.Refresh(eraseBackground = False)
        else:
            l, b, w, h = bbox.get_bounds()
            x = int(l)
            y = int(self.bitmap.GetHeight() - (b+h))
            w = int(w)
            h = int(h)
            self.RefreshRect(wx.Rect(x, y, w, h),
                             eraseBackground = False)
        self.Update() #needed?


I tested these changes with WinXP, python2.5, matplotlib 0.91.2,
wxWidgets2.8.7 with several scripts from the matplotlib/examples and I
could not observe a misbehaviour.

I had to add some calls to figure.canvas.draw in my mpl-embedded-in-wx
application, e.g., after changing a colormap range, to give a
immediate change on screen. Before due to the frequent rerendering I
didn't notice that these statements were missing.

As Chris Barker noticed, Figure.draw() does not lead to a repainting
of the window on screen. This seems to be intended. Instead one should
use pylab.draw() or Figure.canvas.draw().

I thought about a more substantial rewrite of the Wx/WxAgg backend,
similar to the QtAgg backend, but (for the moment) I wanted to try
only simple changes. Anyhow, the Wx backend seems to be in some
aspects outdated (uses old style wx methods, e.g. ToolBar.AddTool) and
is even not fully functional (image support missing). What are the
plans for the future? What about the politics of supporting older
versions of wxWidgets?

Gregor Thalhammer

Christopher Barker schrieb:
> Erik Tollerud wrote:
>   
>> I tested this on 0.91.2 on Ubuntu Gutsy, and wx 2.8.7.1, and found
>> that when I bring up a new window, I see a black canvas and it doesn't
>> draw any of the matplotlib objects until I do something like resizing
>> that must explicitly call a draw at some point.
>>     
>
> yup, same here.
>
> I'm using wxAgg, and FigureCanvas.draw() just doesn't seem to be getting 
> called when I call Figure.draw()
>
> It looks like it's designed to work the other way -- the Window needs to 
> call self.figure.draw(self.renderer) when it wants the image drawn. 
> There is an efficiency to this, as the figure doesn't get rendered until 
> the GUI toolkit needs it. However, having it re-render on every Paint 
> call isn't right either.
>
> So how should this work? I'd like to be able to call Figure.draw(), and 
> have the figure re-draw itself - but then it needs to be able to tell 
> the backend Canvas that it needs to be drawn.
>
> It seems that the figure needs to keep a flag that indicated when it is 
> "dirty", then the paint handler could check that, and call draw if need 
> be. Is there one already?
>
> This all seems a bit awkward though. I've written a bunch of double 
> buffered code, and I try to do it like this:
>
> The Paint handler only blits.
> There is a draw routine that actually draws the off-screen bitmap. It is 
> called:
>    - when the bitmap is new, like in a Re-size event
>    - when the drawing changes.
>
> In the MPL case, then, it seems that figure.draw() should call that draw 
> routine, but maybe it doesn't know anything about its canvas. Ah -- ye 
> sit does - figure.canvas.
>
> OK, so a draw_event is getting called, which I guess is where the 
> drawing should happen, but I'm getting lost now!
>
> -Chris
>   

-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Register now and save $200. Hurry, offer ends at 11:59 p.m., 
Monday, April 7! Use priority code J8TLD2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to