I've run into this same issue in the past, and have it "fixed" in my
own local copy of matplotlib. I just placed a check to make sure that
cid actually was in the callbacks for s before deleting it.
That is quite possibly a band-aid; I never looked far enough in to
see. But it is possibly another way of fixing the problem.
def process(self, s, *args, **kwargs):
process signal *s*. All of the functions registered to receive
callbacks on *s* will be called with *\*args* and *\*\*kwargs*
if s in self.callbacks:
for cid, proxy in self.callbacks[s].items():
# Clean out dead references
if proxy.inst is not None and proxy.inst() is None:
if cid in self.callbacks[s]: #<------- here
On Tue, Oct 25, 2011 at 1:09 PM, <tobin@...> wrote:
> Here is a bit more detail and a simple example.
> The example below places red squares in an axes. When the user clicks on an existing red square - another square is created and added. When the user hits any key a square is deleted from the axes. The error is triggered by clicking on the red square and then hitting any key, and then clicking a red square again.
> By monitoring cbook.py line 235 and cbook.py line 263 it can be seen that after the second mouse click (following one of the squares being deleted), that the process() function builds a loop and begins handling the button press callbacks. Note that there is a dead reference coming later in this list. The first callback involves another square being created and the connect() method being called. In the connect() call - the dead reference is deleted from the callback list. Now upon returning to the process() callback this dead reference is no longer in the callback list and a Key Exception is triggered once it gets to it in the loop.
> There are two locations where dead references are cleared from the callback list. When these loops get intermingled - as the case with a callback leading to another connect mid-loop - the exception occurs when both loops attempt to delete the dead reference.
> Possible Solutions:
> 1. Trap the KeyException at the point of attempting to delete it from the list in both places.
> 2. Place the dead reference check and deletion within a single method ... and perform this check at beginning of the process() and connect() methods before processing callbacks.
> Sample Code
> import matplotlib
> from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
> from matplotlib.pyplot import Figure, Axes, Rectangle
> import wx
> import random
> class SquareManager(object):
> def __init__(self, axes):
> self.axes = axes
> self.canvas = axes.figure.canvas
> self.squares = 
> self.last_x = 0
> self.canvas.mpl_connect('key_press_event', self.on_key_press)
> def add_square(self):
> self.last_x += .1
> s = Square(self, self.axes, [self.last_x, .4],
> .05, .05, facecolor='red', edgecolor='black')
> def on_key_press(self, evt):
> if len(self.squares) == 0: return
> # delete the first square - results in no error
> # self.squares.remove()
> # del self.squares
> # delete the last square - results in the error
> del self.squares[-1]
> def _refresh(self):
> class Square(Rectangle):
> def __init__(self, manager, axes, *args, **kwds):
> Rectangle.__init__(self, *args, **kwds)
> self.manager = manager
> axes.figure.canvas.mpl_connect('button_press_event', self.selected)
> def selected(self, evt):
> within, _ = self.contains(evt)
> if within:
> app = wx.PySimpleApp()
> frame = wx.Frame(None)
> fig = Figure()
> canvas = FigureCanvasWxAgg(frame, -1, fig)
> a = Axes(fig, [.1, .1, .8, .8])
> sm = SquareManager(a)
> # To demonstrate the error:
> # 1. click on red sqaure
> # 2. press any key
> # 3. click on red sqaure again
> The demand for IT networking professionals continues to grow, and the
> demand for specialized networking skills is growing even more rapidly.
> Take a complimentary Learning@... Self-Assessment and learn
> about Cisco certifications, training, and career opportunities.
> Matplotlib-devel mailing list