Pick events on legends work perfectly unless the legend is moved out of the axes using bbox_to_anchor. After that, the pick event gets called twice for a single mouse click.
The following code demonstrates the error. The bug appears when the legend line is clicked: the line toggles twice on a mouse click instead of once.
Toggle plot line-styles between ':' and '-' when line is clicked or
when the corresponding legend line is clicked.
# Create data for plotting
t = numpy.linspace(0, 1.0, 100)
a = numpy.sin(2*numpy.pi*t)
# Set up figure
fig = pylab.figure()
ax = pylab.subplot(111)
# Plot figures
lines = 
for i in range(5):
line = ax.plot(t, (i+1)*a, linestyle=':', picker=5, label='line%d'%(i+1))
lines.append(line) # Save plot lines
# Create legend
leg = ax.legend(bbox_to_anchor=(1.01, 1), loc=2) # Does not work as expected
# leg = ax.legend() # Works!!
# Get legend lines
leglines = leg.get_lines()
# Set event for legend lines
for line in leglines:
# Create a 2 way mapping between legend lines <-> plot lines
line2leg = dict(zip(lines+leglines, leglines+lines))
# Define event function
thisline = event.artist
print ": -> -" # For debugging
print "- -> :" # For debugging
# connect event function
(See also: http://stackoverflow.com/questions/16278358/double-event-registered-on-mouse-click-if-legend-is-outside-axes)
PR #2755 partially fixes this issue.
The main issue is that the picker gets called twice as the legend.get_children() method returns more than just children. Note that this happens regardless of whether the legend is placed in the axes or outside with the bbox_to_anchor. Why doesn't the picker get called twice when inside the axis? Due to the following code
# Pick children
for a in self.get_children():
# make sure the event happened in the same axes
ax = getattr(a, 'axes', None)
if mouseevent.inaxes is None or mouseevent.inaxes == ax:
# we need to check if mouseevent.inaxes is None
# because some objects associated with an axes (e.g., a
# tick label) can be outside the bounding box of the
# axes and inaxes will be None
The offsetbox that legend use as a child does not contain an axes property (perhaps incorrectly so) which means mouseevent.inaxes == ax is not satisfied on the actual legend offsetbox itself resulting in only one call. When a user clicks outside of the axes when the legend is bound outside, mouseevent.inaxes is None holds true so mouseevent.inaxes does not even get evaluated and both get called.
mouseevent.inaxes == ax
mouseevent.inaxes is None
Bottom line, PR #2755 fixes the issue with incorrect children traversal tree and fixes the problem you see here while clicking on the legend outside of the axes but exposes an issue or bug with the picking logic while within the axes.
With both PR #2755 and PR #2756 applied I believe this issue is resolved.
This should be fixed, please re-open if that is not true.