Problem in qtagg and qt4agg backends

A few weeks ago I reported a double draw problem in the qt backends. They
both have a draw() method that looked like this:

    def draw( self ):
        self.replot = True
        FigureCanvasAgg.draw(self)
        self.repaint( False )

It turned out that FCA::draw() and self.repaint() both did a draw which
slowed everything down. Commenting out the FCA draw call seemed to work
fine:

    def draw( self ):
        self.replot = True
        self.repaint( False )

However, this breaks when running code like this:

import pylab as p
p.plot( [1,2,3] )
p.savefig( 'image.png' )

The image is never drawn in this case. If you do a show() and save the
image from the gui, then everything is fine. I did some experimenting and
the solution may be to do this:

    def draw( self ):
        self.replot = True
        FigureCanvasAgg.draw(self)

Which does seem to work for the cases I have. Could someone else take a
look and see if this doesn't break anything (You'll have to edit your local
backends, I haven't changed anything).

Ted

That change causes all kinds of problems with interactive plotting. I tested
the following with ipython -pylab:

a=rand(10,10)
im=imshow(a)
b=rand(10,10)
im.set_data(b)
draw() # nothing happens, lets start over

a=rand(10,10)
im=imshow(a)
b=rand(10,10)
im=imshow(b) # No change, lets close the window and try again
im=imshow(b) # no figure is created
show() # still no figure

I reverted the changes, so we still have an unnecessary call to draw in some
cases, but at least everything should work.

Darren

···

On Thursday 03 April 2008 05:52:28 pm Ted Drain wrote:

A few weeks ago I reported a double draw problem in the qt backends. They
both have a draw() method that looked like this:

    def draw( self ):
        self.replot = True
        FigureCanvasAgg.draw(self)
        self.repaint( False )

It turned out that FCA::draw() and self.repaint() both did a draw which
slowed everything down. Commenting out the FCA draw call seemed to work
fine:

    def draw( self ):
        self.replot = True
        self.repaint( False )

However, this breaks when running code like this:

import pylab as p
p.plot( [1,2,3] )
p.savefig( 'image.png' )

The image is never drawn in this case. If you do a show() and save the
image from the gui, then everything is fine. I did some experimenting and
the solution may be to do this:

    def draw( self ):
        self.replot = True
        FigureCanvasAgg.draw(self)

Which does seem to work for the cases I have. Could someone else take a
look and see if this doesn't break anything (You'll have to edit your local
backends, I haven't changed anything).

Darren Dale wrote:

I reverted the changes, so we still have an unnecessary call to draw in some cases, but at least everything should work.

There are similar issues with the wx back-end (see the recent thread). The proposed patch to wx should be tested in interactive mode. If it works, then maybe it can be ported to QT. If it doesn't then this makes it quite clear that some re-factoring is needed:

As I looked through the wx code, I found it to be a bit of a jumbled mess of blitting and drawing and... It looks like it has grown out of a simpler system, with various patches to fix things that didn't work, eventually ending up with "re-draw every time you paint", which should work (and does), but really kills the whole idea of double buffering.

Anyway, it should be structured something like:

In the Paint handler:
Only a blit of the buffer bitmap -- that it, ever. In the case of the Agg back-ends, I don't know if there should be both the Agg bitmap and a native (wx, qt, or gtk) bitmap -- I suppose that's a function of how fast it is to go from agg to native bitmap -- I think this can be now done on wx without any data copying, so it should be fast enough. If not, then the native and agg bitmaps should always be kept in sync.

When should the bitmap be re-drawn?

1) On a Size event -- so a Size event handler needs to call canvas.draw() (is that right?)

2) when the users code or MPL asks for a re-draw -- this is the canvas.draw() call, I think.

It should be as simple as that! Or are there other times that the bitmap needs to be re-drawn?

One complication -- when the canvas is re-drawn due to a user action, the screen needs to be updated, so we need a "blit the bitmap to the screen now" call, which would be made after every canvas.draw() call, unless it's a SaveFig or printing call. On wx, the theory is that you can call Refresh() and Update(), which will trigger a Paint event. However, I've found that that sometimes doesn't happen right away -- it's fast enough for common use, but not for animation, so I suggest that there be a BlitNow() method that uses a wx.ClientDC to blit the bitmap. I don't know about QT or GTK.

I have noticed a bunch of code that computes the damaged region of the window, so that only that part gets blitted -- in theory that's a nice optimization, but in practice, I've found that bltting the whole window is plenty fast, so there's little point.

I wish I had more time to work on this...

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer

NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception