您好。这个问题是"为什么图例拾取只适用于"ax.twinx(("而不适用于"ax"?"的后续问题?。
下面提供的最小代码分别在ax1
和ax2 = ax1.twinx()
上绘制了两条曲线,创建了它们的图例框,并将底部图例移动到顶部ax,以便可以使用选取器事件。单击图例项将隐藏/显示关联的曲线。
如果使用ax.scatter(...)
,则工作正常如果改为使用ax.plot(...)
,则图例拾取会突然中断。为什么?其他什么都没有改变,所以这很令人困惑。我测试了其他几种绘图方法,但都没有达到预期效果。
以下是它的动作视频:https://imgur.com/qsPYHKc.mp4
import matplotlib.pyplot as plt
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)
# This is a quick way to change the plotting function, simply modify n.
n = 0
function, container = [("scatter", "collections"),
("plot", "lines"),
("bar", "patches"),
("barbs", "collections"),
("quiver", "collections")][n]
getattr(ax1, function)(X, Y1, color="green", label="$Y_1$")
getattr(ax2, function)(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)
for n, legend in enumerate((legend1, legend2)):
legend_item = legend.legendHandles[0]
legend_item.set_gid(n+1)
legend_item.set_picker(10)
# 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]
for artist in getattr(ax, container):
artist.set_visible(not artist.get_visible())
plt.draw()
fig.canvas.mpl_connect("pick_event", on_graph_pick_event)
好的,所以我知道这不是答案,但评论不允许我进行这种头脑风暴。我试了几件事,注意到了以下几点。当您在for循环中打印legendHandles
艺术家的axes
时,在散点图/PathCollection
艺术家的情况下,它会为两个图例返回None
。然而,在"正常"情节/Line2D
艺术家的情况下,它返回轴对象!更重要的是;即使在终端中,它们的表示似乎是相同的(AxesSubplot(0.125,0.11;0.775x0.77)
(,如果您检查它们是否是== ax2
,对于legend1
的legendHandles
艺术家,它返回False
,而对于legend2
的艺术家,它则返回True
。这里发生了什么?
因此,我不仅尝试从ax1
中删除legend1
并将其再次添加到ax2
中,而且还尝试对legendHandles
对象执行同样的操作。但它不允许我这么做:
NotImplementedError: cannot remove artist
在我看来,你发现了一个bug,或者至少是不一致的行为。这是我迄今为止尝试的代码,以防其他人想进一步使用它。
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()