我有几个matplolib.pyplot
数字。每个都有一个图例,我想要的是单击图例上的线以隐藏图中的线。点击事件处理位于以下位置:https://matplotlib.org/examples/event_handling/legend_picking.html
当只有一个数字时,此方法工作正常,但当有多个数字时,仅适用于最后一个数字。当我单击另一个图例的图例时,没有任何异常或警告,但是什么也没有发生。
以下是具有此问题的示例代码:
import matplotlib.pyplot as plt
import numpy as np
a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)
lined = {}
for var1, var2 in [(a,b), (c,d)]:
fig, ax = plt.subplots()
line1, = ax.plot(var1, label="l1")
line2, = ax.plot(var2, label="l2")
leg = fig.legend([line1, line2], ["l1", "l2"])
legl1, legl2 = leg.get_lines()
legl1.set_picker(5)
lined[legl1] = line1
legl2.set_picker(5)
lined[legl2] = line2
def onpick(event, figu):
legl = event.artist
origl = lined[legl]
vis = not origl.get_visible()
origl.set_visible(vis)
if vis:
legl.set_alpha(1.0)
else:
legl.set_alpha(0.2)
figu.canvas.draw()
fig.canvas.mpl_connect('pick_event', lambda ev: onpick(ev, fig))
plt.show()
如何使click事件也适用于第一个数字?
答案 0 :(得分:1)
原因由https://docs.python-guide.org/writing/gotchas/#late-binding-closures给出。我必须承认我自己并不完全理解它,但是它提供了解决问题的技巧:使用默认参数。
import matplotlib.pyplot as plt
import numpy as np
a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)
lined = {}
for var1, var2 in [(a,b), (c,d)]:
fig, ax = plt.subplots()
line1, = ax.plot(var1, label="l1")
line2, = ax.plot(var2, label="l2")
leg = fig.legend([line1, line2], ["l1", "l2"])
legl1, legl2 = leg.get_lines()
legl1.set_picker(5)
lined[legl1] = line1
legl2.set_picker(5)
lined[legl2] = line2
def onpick(event, figu=fig):
legl = event.artist
origl = lined[legl]
vis = not origl.get_visible()
origl.set_visible(vis)
if vis:
legl.set_alpha(1.0)
else:
legl.set_alpha(0.2)
figu.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick) # no need for a lambda
plt.show()
如前所述,该解决方案有点不可靠。比较看似等效的
# works
def onpick(event, figu=fig):
(...)
figu.canvas.draw() # using a default arg equal to fig
fig.canvas.mpl_connect('pick_event', onpick)
vs。
# fails as described
def onpick(event):
(...)
fig.canvas.draw() # using fig from main loop directly
fig.canvas.mpl_connect('pick_event', onpick)
答案 1 :(得分:1)
@Leporello's answer就诊断和解决方案而言是完整的。这是完整的解释:
第lambda ev: onpick(ev, fig)
行创建了一个引用名称onpick
和fig
的函数对象。在循环运行plt.show()
之后,循环结束后,函数称为。
名称onpick
和fig
是非本地的,因此它在模块名称空间中搜索。在调用侦听器时,onpick
指的是创建的最后一个函数,fig
指的是在循环的最后一次迭代中创建的图形。
Leporello关于默认参数的建议可能是执行此操作最优雅的方法。之所以有效,是因为立即评估了整个def
语句。 def
创建一个内部带有代码块的功能对象,然后在该位置和那里分配对默认值的引用。这意味着您最终将回调设置为正确的函数对象,而循环内的fig
就是figu
指向的内容。
任何将名称onpick
和fig
绑定到回调的本地名称空间或循环中创建的任何其他名称空间的操作将解决您的问题。
答案 2 :(得分:0)
这是一个完美的应用程序,其中使用类可能会有所帮助。它将允许将各个图形存储在实例变量中,并在类的方法中使用它。
import matplotlib.pyplot as plt
import numpy as np
class MyPlot():
def __init__(self, var1, var2):
self.lined = {}
self.fig, ax = plt.subplots()
line1, = ax.plot(var1, label="l1")
line2, = ax.plot(var2, label="l2")
leg = self.fig.legend([line1, line2], ["l1", "l2"])
legl1, legl2 = leg.get_lines()
legl1.set_picker(5)
self.lined[legl1] = line1
legl2.set_picker(5)
self.lined[legl2] = line2
self.cid = self.fig.canvas.mpl_connect('pick_event', self.onpick)
def onpick(self, event):
legl = event.artist
if legl in self.lined:
origl = self.lined[legl]
vis = not origl.get_visible()
origl.set_visible(vis)
if vis:
legl.set_alpha(1.0)
else:
legl.set_alpha(0.2)
self.fig.canvas.draw()
a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)
plots = [MyPlot(*var) for var in [(a,b), (c,d)]]
plt.show()
答案 3 :(得分:0)
实际上,我找到了一个比ImportanceOfBeingErnest's更简单并且比Leporello's更有意义的解决方案,因为它只定义了一次函数onpick
,并且每个图形都相同
由于仅canvas
才需要引用该图,并且canvas
中可以找到event
,因此以下代码可以完美地工作:
import matplotlib.pyplot as plt
import numpy as np
a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)
lined = {}
def onpick(event):
legl = event.artist
origl = lined[legl]
vis = not origl.get_visible()
origl.set_visible(vis)
if vis:
legl.set_alpha(1.0)
else:
legl.set_alpha(0.2)
event.canvas.draw()
for var1, var2 in [(a,b), (c,d)]:
fig, ax = plt.subplots()
line1, = ax.plot(var1, label="l1")
line2, = ax.plot(var2, label="l2")
leg = fig.legend([line1, line2], ["l1", "l2"])
legl1, legl2 = leg.get_lines()
legl1.set_picker(5)
lined[legl1] = line1
legl2.set_picker(5)
lined[legl2] = line2
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()