Skip to content Skip to sidebar Skip to footer

Legend Picking Breaks When Drawing `plot`s Instead Of `scatter`s

Good day. This question is a follow-up of Why does legend-picking only works for `ax.twinx()` and not `ax`?. The minimal code provided below plots two curves respectively on ax1 an

Solution 1:

Ok, so I know this is not the answer, but the comments don't allow me to do this kind of brainstorming. I tried a couple of things, and noticed the following. When you print the axes of the legendHandles artists in your for loop, it returns None for both legends in the case of the scatter plot / PathCollection artists. However, in the case of the 'normal' plot / Line2D artists, it returns axes objects! And even more than that; even though in the terminal their representations seem to be the same (AxesSubplot(0.125,0.11;0.775x0.77)), if you check if they are == ax2, for the legendHandles artist of legend1 it returns False, while for the one of legend2, it returns True. What is happening here?

So I tried to not only remove legend1 from ax1 and add it again to ax2 but to also do the same with the legendHandles object. But it doesn't allow me to do that:

NotImplementedError: cannot remove artist

To me it looks like you found a bug, or at least inconsistent behaviour. Here is the code of what I tried so far, in case anybody else would like to play around with it further.

import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Qt5Agg')
import numpy as np

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

X = np.linspace(0, 2*np.pi, 100)
Y1 = X**0.5 * np.sin(X)
Y2 = -np.cos(X)

USE_LINES = True  # <--- set this to True or False to test both cases.
if USE_LINES:
    ax1.plot(X, Y1, color="green", label="$Y_1$")
    ax2.plot(X, Y2, color="red",   label="$Y_2$")
else:
    ax1.scatter(X, Y1, color="green", label="$Y_1$")
    ax2.scatter(X, Y2, color="red",   label="$Y_2$")

# Put both legends on ax2 so that pick events also work for ax1's legend.
legend1 = ax1.legend(loc="upper left")
legend2 = ax2.legend(loc="upper right")
legend1.remove()
ax2.add_artist(legend1)
# legend1.legendHandles[0].remove()
# ax2.add_artist(legend1.legendHandles[0])

for n, legend in enumerate((legend1, legend2)):
    legend_item = legend.legendHandles[0]
    legend_item.set_gid(n+1)
    legend_item.set_picker(10)
    print(
        f'USE_LINES = {USE_LINES}', f'legend{n+1}',
        legend_item.axes.__repr__() == legend.axes.__repr__(),
        legend_item.axes == legend.axes,
        legend_item.axes.__repr__() == ax2.__repr__(),
        legend_item.axes == ax2, type(legend_item),
    )

# When a legend element is picked, hide/show the associated curve.
def on_graph_pick_event(event):

    gid = event.artist.get_gid()
    print(f"Picked Y{gid}'s legend.")

    ax = {1: ax1, 2: ax2}[gid]
    artist = ax.lines[0] if USE_LINES else ax.collections[0]
    artist.set_visible(not artist.get_visible())
    plt.draw()

fig.canvas.mpl_connect("pick_event", on_graph_pick_event)
plt.show()

Post a Comment for "Legend Picking Breaks When Drawing `plot`s Instead Of `scatter`s"