Strange event errors with WXAgg + wx

Hello. I'm trying to embed mpl in an wxpython app which is intended
to run repeatedly within a single script. (It's a workaround for not
being able to use pyplot.show() more than once in a script.)

All seems to work as intended when I don't attach any key press events
(here to just destroy the app) to canvas, but when I do, as in the
script below, for some reason I keep getting errors that appear to
indicate that some remnant of the zombie of the previous app instance
may be sticking around when they shouldn't exist. Both the script in
question and the (typical) error message are shown below in this
message.

I've tried both on Linux (Debian Lenny) and Mac OS X (Leopard) with
Python 2.5 with the latest stable versions of numpy, matplotlib,
wxpython and whatnot. I observe similar problems on both
environments. What am I doing wrong? Am I not properly handling the
cleanup process before destroying the app at each loop? I think
wx.App.MainLoop() can be run more than once within a script, so that
should be okay, though I'm not sure if mpl is designed to be used this
way.

A strange thing is that when I close the app by clicking on the 'X' on
the Window frame, I don't reproduce the error. If anything, I wonder
how to initiate the event that imitates that behavior, but that is
probably a question for the wxpython people...

I'd appreciate any assistance!

Cheers,
Taro

#### BEGIN SCRIPT ####

#!/usr/bin/env python

import wxversion
wxversion.ensureMinimal('2.8')
import matplotlib
matplotlib.use('WXAgg')

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
from numpy import arange, sin, pi
import wx

class CanvasFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'CanvasFrame',size=(550,350))

        self.figure = Figure()
        self.axes = self.figure.add_subplot(111)
        t = arange(0.0, 3.0, 0.01)
        s = sin(2 * pi * t)

        self.axes.plot(t, s)
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

        self.canvas.mpl_connect('key_press_event', self.key_press)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.sizer)
        self.Fit()

    def key_press(self, event):
        self.Close(True)

    def OnCloseWindow(self, event):
        print 'Window Closed'
        self.Destroy()

class App(wx.App):

    def OnInit(self):
        frame = CanvasFrame()
        frame.Show(True)
        return True

def run_app():
    app = App(redirect=False)
    app.MainLoop()
    del app

for i in range(10):
    run_app()

#### END SCRIPT ####

#### BEGIN ERROR MESSAGE ####

Traceback (most recent call last):
  File "/usr/lib/python2.5/site-packages/matplotlib/backends/backend_wx.py",
line 1316, in _onMotion
    FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=evt)
  File "/usr/lib/python2.5/site-packages/matplotlib/backend_bases.py",
line 1244, in motion_notify_event
    guiEvent=guiEvent)
  File "/usr/lib/python2.5/site-packages/matplotlib/backend_bases.py",
line 899, in __init__
    LocationEvent.__init__(self, name, canvas, x, y, guiEvent=guiEvent)
  File "/usr/lib/python2.5/site-packages/matplotlib/backend_bases.py",
line 817, in __init__
    self._update_enter_leave()
  File "/usr/lib/python2.5/site-packages/matplotlib/backend_bases.py",
line 844, in _update_enter_leave
    last.canvas.callbacks.process('axes_leave_event', last)
  File "/usr/lib/python2.5/site-packages/wx-2.8-gtk2-unicode/wx/_core.py",
line 14314, in __getattr__
    raise PyDeadObjectError(self.attrStr % self._name)
wx._core.PyDeadObjectError: The C++ part of the FigureCanvasWxAgg
object has been deleted, attribute access no longer allowed.

#### END ERROR MESSAGE ####

Taro Sato-4 wrote:

Hello. I'm trying to embed mpl in an wxpython app which is intended
to run repeatedly within a single script. (It's a workaround for not
being able to use pyplot.show() more than once in a script.)

All seems to work as intended when I don't attach any key press events
(here to just destroy the app) to canvas, but when I do, as in the
script below, for some reason I keep getting errors that appear to
indicate that some remnant of the zombie of the previous app instance
may be sticking around when they shouldn't exist. Both the script in
question and the (typical) error message are shown below in this
message.

I think I figured out what causes wx._core.PyDeadObjectError above.

In backend_bases.py, LocationEvent class has the attribute lastevent
(initially set to None) which keeps the Event instance of a previous event.
LocationEvent._update_enter_leave() method processes axes_leave_event for
lastevent if not None, and then processes axes_enter_event for the current
event.

When an application window first gets created, lastevent is set to None.
When the application is destroyed and a new instance of the application is
created, lastevent needs to be reset to None, which apparently is not
happening. The reference to the previous event which occurred within the
destroyed application is being accessed via lastevent, hence the
PyDeadObjectError.

I'm not sure what the best timing is to rest lastevent (I'm not thoroughly
familiar with mpl source codes). But modifying
LocationEvent._update_enter_leave() such that the PyDeadObjectError
exception raised by accessing lastevent solves the specific issue in the
original post.

Should I file a bug report on this?

Cheers,
Taro

···

--
View this message in context: http://old.nabble.com/Strange-event-errors-with-WXAgg-%2B-wx-tp26312977p26355887.html
Sent from the matplotlib - users mailing list archive at Nabble.com.