[英]Create legend click events on multiple matplotlib.pyplot subplots
我有幾個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事件也適用於第一個圖形?
原因由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)
與
# fails as described
def onpick(event):
(...)
fig.canvas.draw() # using fig from main loop directly
fig.canvas.mpl_connect('pick_event', onpick)
在診斷和解決方案方面, @ Leporello的答案是完整的。 這是完整的解釋:
lambda ev: onpick(ev, fig)
行lambda ev: onpick(ev, fig)
創建一個函數對象,該函數對象引用名稱onpick
和fig
。 在plt.show()
運行之后 ,循環結束后調用該函數。
名稱onpick
和fig
是非本地的,因此它在模塊名稱空間中搜索。 在調用偵聽器時, onpick
指的是創建的最后一個函數,而fig
指的是在循環的最后一次迭代中創建的圖。
Leporello關於默認參數的建議可能是最優雅的方法。 之所以有效,是因為立即評估了整個def
語句。 def
會創建一個內部帶有代碼塊的功能對象,然后在此位置將引用分配給默認對象。 這意味着您最終將回調設置為正確的函數對象,並且循環中的任何fig
都將成為figu
指向的對象。
將名稱onpick
和fig
綁定到回調的本地名稱空間中的任何操作或在循環中創建的任何其他名稱空間的任何操作都可以解決您的問題。
這是一個完美的應用程序,其中使用類可能是有益的。 它將允許將各個圖形存儲在實例變量中,並在類的方法中使用它。
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()
實際上,我發現了一個比ImportanceOfBeingErnest的解決方案簡單一些的解決方案,並且比Leporello的解決方案更有意義,因為它只定義了onpick
函數一次,並且每個圖都相同。
由於僅對canvas
才需要引用該圖,並且可以在event
找到canvas
,因此以下代碼可以完美地工作:
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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.