thanks for your valueable feedback. I am proceeding slowly, but steadily.
> 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.
It still feels a bit kludgy to me -- a paint event should simply copy the bitmap to the screen, any re-rendering should be triggered by other events -- either a re-size, or explicitly by figure.canvas.draw() or something. Anyway, given the current structure, this looks like the way to go.
I agree, this is currently more a workaround for some missing rererenderings. It seems to me, that rendering the figure is avoided as much as possible. Possibly this is due to the support for the vector graphic backends (postscript, pdf, svg)? I guess that with these backends rendering means actually creating the output file, but I didn't have a look at the source code.
self._need_rerender = True
Where does this get set to True again? On a Figure.canvas.draw() call?
Actually nowhere else. In the QtAgg backend, in Figure.canvas.draw this is set to True, and then a repaint event is triggered, but no explicit rerendering. I didn't get this fact from the beginning. As stated above, this command is essentially a workaround for a missing initial rerendering after creating the FigureCanvas.
changed _onPaint(...) to following (note: removed evt.Skip() at end!)
def _onPaint(self, evt):
#repaint only damaged parts of window
I don't know that this is needed, bitting the whole Window is blazingly fast anyway -- but if it works, why not?
Actually, this code repaints the bounding box containing all damaged regions. I did it because the QtAgg backend also did it like this. If I quickly move another window in front of a matplotlib figure, than I can see I small difference.
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).
Well, it's needed whenever the figure changes -- on each figure.canvas.draw() call, I guess.
You are right. To express it more clearly: In my changed code a rererendering of the bitmap is done on each figure.canvas.draw() (as before). In the onPaint event callback no rerendering is done except the very first it's get callad after the figure has been created (changed behaviour).
> I moved code from gui_repaint() into
_onPaint. Calls to gui_repaint() in other methods (e.g., draw) might now be
self.Update() #this is optional, leeds to an immediate repaint
Maybe -- I've found (at least on OS-X) that using a ClientDC is still required sometimes to get instant response. This is key if you're doing anything like animation.
Initially I thought this is optional and might avoid some unnecessary repainting. Later I discovered that it is crucial for interactive panning. And for animation.
def draw(self, repaint=True):
Render the figure using agg.
DEBUG_MSG("draw()", 1, self)
self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
self.Refresh(eraseBackground = False)
I think maybe these should be calls to gui_repaint, which will get you to a ClientDC instead of waiting for a paint event -- Update is not always instant.
Didn't know this. The wxWidgets documentation states about Update(): "Calling this method immediately repaints the invalidated area of the window..."
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.
I agree -- I think I'm going to need to add a few of those too. The problem is that this is a change, and other folks' code is going to break too.
At least for my case I would say that these changes to the wx backend didn't break my code, they revealed mistakes in my code (I used Refresh() instead of figure.canvas.draw() calls to get a rerendering of the matplotlib figure).
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 think you're right -- I should have looked at the pylab.draw code to see what it did. Though I think Figure should have a method that does do an instant update...DrawNow??
For GUI backends I would even expect that Figure.draw() should update the figure on screen. However, I don't know how this should be implemented for not breaking code which uses other backends.
What about the politics of supporting older versions of wxWidgets?
I wouldn't bother, but I'm a bleeding-edge kind of guy. It seems that we could at least make sure not to break anything by keeping the old code around for older versions, and go all 2.8 for new code, with one big 'ol version test at the top of the modules.
Did I understand you correctly: This means to drop support for ancient versions of wx in newly added code? This makes developing much easier.