pan/zoom go invisible in MPL plot in wx2.9

I have an odd issue that appears to be a wxPython issue, though it might be a problem that lies somewhere between wx and Matplotlib…

I have a Matplotlib plot embedded in aui.AuiNotebook (though it could also be a wx.aui.AuiNotebook) in a wxPython application. Using wxPython 2.8.10, I don’t see the issue. But I want things to, for now, work on both 2.8 and 2.9, and am having problems with 2.9…

The Problem: Using wxPython 2.9.4, the pan and zoom tools of the Matplotlib toolbar work normally at first, but if I split up the notebook by adding a new page, close both the plot page and the newly added page, and then create a new plot, there are at least two problems with displaying the actions of these tools:

  • while zooming, the rectangular “rubber band” that represents the to-be-zoomed area is no longer visible
  • panning doesn’t update the plot in real time, but the plot jumps to the panned location after you release the mouse.

So it seems that something about “updating” the canvas of the plot has been altered by the fact of splitting a previous aui.AuiNotebook.

Any clues? I don’t have a small, runnable sample yet, but thought I’d check if anyone has run into anything like this or if someone has a clue for where to even begin to look. I thought I’d start by asking the wxPython list, since it does seem to be a difference in 2.8 vs. 2.9.

Thanks,
Che

Hi,
I am not sure, whether it could be some similar issue; but I also
experienced some buggy behaviour in matplotlib using the wx backend
and the recent wxpython 2.9 (in contrast to 2.8, which appeared ok.
I wasn't able to find anything specific in the sources, but the
general advice I got - i.e. using the wxagg backend seems to solve
this issues.
You may check the previous posts:
https://groups.google.com/forum/#!msg/wxpython-users/vG-5NJ4vXnc/tSVCFh9XInUJ
http://matplotlib.1069221.n5.nabble.com/matplotlib-backends-WXAgg-vs-WX-td6189.html

(The strange thing is, this regression for matplotlib seems to come
between wxpython 2.9.1.1 and 2.9.2.1, but I couldn't investigate it
further.)

regards,
    vbr

···

2013/1/30 C M <cmpython@gmail.com>:

I have an odd issue that appears to be a wxPython issue, though it might be
a problem that lies somewhere between wx and Matplotlib...

I have a Matplotlib plot embedded in aui.AuiNotebook (though it could also
be a wx.aui.AuiNotebook) in a wxPython application. Using wxPython 2.8.10,
I don't see the issue. But I want things to, for now, work on both 2.8 and
2.9, and am having problems with 2.9...

The Problem: Using wxPython 2.9.4, the pan and zoom tools of the Matplotlib
toolbar work normally at first, but if I split up the notebook by adding a
new page, close both the plot page and the newly added page, and then create
a new plot, there are at least two problems with displaying the actions of
these tools:

- while zooming, the rectangular "rubber band" that represents the
to-be-zoomed area is no longer visible
- panning doesn't update the plot in real time, but the plot jumps to the
panned location after you release the mouse.

So it seems that something about "updating" the canvas of the plot has been
altered by the fact of splitting a previous aui.AuiNotebook.

Any clues? I don't have a small, runnable sample yet, but thought I'd check
if anyone has run into anything like this or if someone has a clue for where
to even begin to look. I thought I'd start by asking the wxPython list,
since it does seem to be a difference in 2.8 vs. 2.9.

Thanks,
Che

--
--
To unsubscribe, send email to wxPython-users+unsubscribe@googlegroups.com
or visit http://groups.google.com/group/wxPython-users?hl=en
---
You received this message because you are subscribed to the Google Groups
"wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

CORRECTION: It appears to be happening on both wx2.8.10.1 and wx2.9.4. Sorry about my confusion before. I have to check if I changed anything to explain why I thought it was doing it on one and not the other–in any case, both do it now.

Again, any hints as to what might underlie this? (Also, using WinXP, Python 2.5 or 2.7, respectively with 2.8.10.1 and 2.9.4)

Thanks,
Che

···

On Wed, Jan 30, 2013 at 5:36 PM, C M cmpython@gmail.com wrote:

I have an odd issue that appears to be a wxPython issue, though it might be a problem that lies somewhere between wx and Matplotlib…

I have a Matplotlib plot embedded in aui.AuiNotebook (though it could also be a wx.aui.AuiNotebook) in a wxPython application. Using wxPython 2.8.10, I don’t see the issue. But I want things to, for now, work on both 2.8 and 2.9, and am having problems with 2.9…

Hi,

I am not sure, whether it could be some similar issue; but I also

experienced some buggy behaviour in matplotlib using the wx backend

and the recent wxpython 2.9 (in contrast to 2.8, which appeared ok.

I wasn’t able to find anything specific in the sources, but the

general advice I got - i.e. using the wxagg backend seems to solve

this issues.

You may check the previous posts:

https://groups.google.com/forum/#!msg/wxpython-users/vG-5NJ4vXnc/tSVCFh9XInUJ

http://matplotlib.1069221.n5.nabble.com/matplotlib-backends-WXAgg-vs-WX-td6189.html

(The strange thing is, this regression for matplotlib seems to come

between wxpython 2.9.1.1 and 2.9.2.1, but I couldn’t investigate it

further.)

Thanks, Vlastimil. I am trying the various backends (WxAgg, Agg, and WX) and so far don’t see any difference among them. Thanks for the links.

The key to this is understanding how the pan and zoom tools’ actions are drawn on the screen and what could affect that. I’ll keep trying…

Che

OK, I figured it out. The second page in the auiNotebook used SetCompositeMode() (search this archive for Robin’s code on that), and that was being called on the whole frame, and somehow that prevented the updating of the Matplotlib canvas in terms of the zoom rubber band and panning. In fact, once the that SetCompositeMode() had been called on the frame, closing the page had no effect, as it had already been set on the frame and that “stuck”. I think.

Esoteric and probably not helpful to anyone else, but I thought I’d just put the solution here.

Che

C M wrote:

OK, I figured it out. The second page in the auiNotebook used
SetCompositeMode() (search this archive for Robin's code on that), and
that was being called on the whole frame, and somehow that prevented the
updating of the Matplotlib canvas in terms of the zoom rubber band and
panning. In fact, once the that SetCompositeMode() had been called on
the frame, closing the page had no effect, as it had already been set on
the frame and that "stuck". I think.

Esoteric and probably not helpful to anyone else, but I thought I'd just
put the solution here.

Yes, setting DoubleBuffered/Composite mode on Windows can make it so drawing done via a wx.ClientDC is lost and never actually seen. You really need to do all drawing in the paint event in that case, or use wx.Overlay for transient drawing.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org

It would be great if someone could re-factor the MPL wx back-end to
use this approach (ideally the overlay version) -- last I looked, the
wx back-end was a bit of a mess, in terms of when/how the screen got
updated -- very hard to keep track of, and I think a number of
unnecessary updates occurred - each to fix another bug, I suppose --
beter to update too often than not often enough.

But if someone want to clear it up -- that would be great! It's been
on my list a long time, but honestly I don't see it floating to the
top for along time -- if ever.

-Chris

···

On Fri, Feb 1, 2013 at 9:56 AM, Robin Dunn <robin@alldunn.com> wrote:

Yes, setting DoubleBuffered/Composite mode on Windows can make it so drawing
done via a wx.ClientDC is lost and never actually seen. You really need to
do all drawing in the paint event in that case, or use wx.Overlay for
transient drawing.

--

Christopher Barker, Ph.D.
Oceanographer

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

Chris.Barker@noaa.gov

Responding to my own very old thread…

For some reason, now that I have upgraded last week to wxPython 4.1.0 and Matplotlib 2.2.5 (from previously a wxPython wxPython 3.0.2.0 and Matplotlib 1.1.3), I’m having this same problem again as I had years back.

The problem is the same as listed in the original thread: When I go to zoom in on a Matplotlib graph, the “rubberband” doesn’t show. This was originally due to my using SetCompositeMode() on the frame that this Matplotlib canvas is ultimately within, which means ClientDC isn’t shown. As Robin said:

Yes, setting DoubleBuffered/Composite mode on Windows can make it so drawing done via a wx.ClientDC is lost and never actually seen. You really need to do all drawing in the paint event in that case, or use wx.Overlay for transient drawing.

However, it was working last week before I upgraded wxPython and Matplotlib. I’m not sure what changed and what I need to do.

It seems I need to keep the SetCompositeMode() on the frame because if I don’t, I get some ugly black/damaged areas in the page of the aui.AuiNotebook. But maybe not anymore? Any other way to avoid this damage?

Below I will give the code for the draw_rubberband function in Matplotlib 1.1.3 and now the new one I am using in 2.2.5.

Robin, can you suggest what I can do? Thanks!

Matplotlib 1.1.3’s draw_rubberband function:

def draw_rubberband(self, event, x0, y0, x1, y1):
    'adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744'
    canvas = self.canvas
    dc =wx.ClientDC(canvas)

    # Set logical function to XOR for rubberbanding
    dc.SetLogicalFunction(wx.XOR)

    # Set dc brush and pen
    # Here I set brush and pen to white and grey respectively
    # You can set it to your own choices

    # The brush setting is not really needed since we
    # dont do any filling of the dc. It is set just for
    # the sake of completion.

    wbrush =wx.Brush(wx.Colour(255,255,255), wx.TRANSPARENT)
    wpen =wx.Pen(wx.Colour(200, 200, 200), 1, wx.SOLID)
    dc.SetBrush(wbrush)
    dc.SetPen(wpen)

    dc.ResetBoundingBox()
    dc.BeginDrawing()
    height = self.canvas.figure.bbox.height
    y1 = height - y1
    y0 = height - y0

    if y1<y0: y0, y1 = y1, y0
    if x1<y0: x0, x1 = x1, x0

    w = x1 - x0
    h = y1 - y0

    rect = int(x0), int(y0), int(w), int(h)
    try: lastrect = self.lastrect
    except AttributeError: pass
    else: dc.DrawRectangle(*lastrect)  #erase last
    self.lastrect = rect
    dc.DrawRectangle(*rect)
    dc.EndDrawing()

Matplotlib 2.2.5’s draw_rubberband function:

def draw_rubberband(self, event, x0, y0, x1, y1):
    if self.retinaFix:  # On Macs, use the following code
        # wx.DCOverlay does not work properly on Retina displays.
        rubberBandColor = '#db1818' #'#C0C0FF'
        if self.prevZoomRect:
            self.prevZoomRect.pop(0).remove()
        self.canvas.restore_region(self.savedRetinaImage)
        X0, X1 = self.zoomStartX, event.xdata
        Y0, Y1 = self.zoomStartY, event.ydata
        lineX = (X0, X0, X1, X1, X0)
        lineY = (Y0, Y1, Y1, Y0, Y0)
        self.prevZoomRect = self.zoomAxes.plot(
            lineX, lineY, '-', color=rubberBandColor)
        self.zoomAxes.draw_artist(self.prevZoomRect[0])
        self.canvas.blit(self.zoomAxes.bbox)
        return

    # Use an Overlay to draw a rubberband-like bounding box.

    dc = wx.ClientDC(self.canvas)
    odc = wx.DCOverlay(self.wxoverlay, dc)
    odc.Clear()

    # Mac's DC is already the same as a GCDC, and it causes
    # problems with the overlay if we try to use an actual
    # wx.GCDC so don't try it.
    if 'wxMac' not in wx.PlatformInfo:
        dc = wx.GCDC(dc)

    height = self.canvas.figure.bbox.height
    y1 = height - y1
    y0 = height - y0

    if y1 < y0:
        y0, y1 = y1, y0
    if x1 < x0:
        x0, x1 = x1, x0

    w = x1 - x0
    h = y1 - y0
    rect = wx.Rect(x0, y0, w, h)

    rubberBandColor = '#db1818' #'#C0C0FF'  # or load from config?

    # Set a pen for the border
    color = wxc.NamedColour(rubberBandColor)
    dc.SetPen(wx.Pen(color, 1))

    # use the same color, plus alpha for the brush
    r, g, b, a = color.Get(True)
    color.Set(r, g, b, 0x60)
    dc.SetBrush(wx.Brush(color))
    if wxc.is_phoenix:
        dc.DrawRectangle(rect)
    else:
        dc.DrawRectangleRect(rect)

···

You may want to try this code:

It moves all the painting into the EVT_PAINT handler.

Thank you. But I don’t use GitHub and am not sure where to find the code. Is there a file that I could just drop in to replace backend_wx that would work with the following?

  • wxPython 4.1.0
  • Matplotlib 2.2.5
  • Python 2.7

Well, you could look at the changes here and try to apply them manually to your version: wx backends: don't use ClientDC any more by DietmarSchwertberger · Pull Request #11944 · matplotlib/matplotlib · GitHub

I think that’s doable. The code is probably not too different.