2013-04-29 15 views
2

我必須在一個繪圖中繪製幾個數據集。能夠突出顯示一個或多個圖表以便比較它們非常有用。爲此,無論何時直接選擇一條線,或通過單擊圖例中的相應條目,我都會在":"(背景圖)和"-"(高亮顯示的圖)之間切換圖的線型。如果圖例不在座標軸上,鼠標點擊就會註冊雙擊事件

這個工作完美,直到我嘗試使用bbox_to_anchor移動軸外的圖例。在此之後,只需點擊圖例行上的一個鼠標就會連續觸發2個點擊事件,從而取消切換效果。

如何在保持pick_event的正確行爲的同時將圖例置於軸外?

重現問題的簡化代碼(單擊一條繪圖線在「高亮顯示」和「未高亮顯示」之間切換,而單擊圖例線在返回之前狀態之前短暫切換繪圖線):

import pylab 
import numpy 

# 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[0]) # 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: 
    line.set_picker(5) 

# Create a 2 way mapping between legend lines <-> plot lines  
line2leg = dict(zip(lines+leglines, leglines+lines)) 

# Define event function 
def onpick(event): 
    thisline = event.artist 

    if thisline.get_linestyle()==':': 
     print ": -> -" # For debugging 
     thisline.set_linestyle('-') 
     line2leg[thisline].set_linestyle('-') 
    else: 
     print "- -> :" # For debugging 
     thisline.set_linestyle(':') 
     line2leg[thisline].set_linestyle(':') 
    fig.canvas.draw() 

# connect event function  
fig.canvas.mpl_connect('pick_event', onpick) 
pylab.show() 
+0

這很好奇。它看起來像是同一個選擇事件被處理了兩次,而不是它產生了兩個事件,這讓我認爲這是一個錯誤 – tacaswell 2013-04-29 14:49:19

+0

@tcaswell如何判斷它是否是同一個選擇事件,或者是否是同一類型的事件事件產生? – Dhara 2013-04-29 15:06:57

+0

如果您向'onpick'添加'print event'行,它們在打印時具有相同的內存地址 – tacaswell 2013-04-29 15:23:07

回答

3

如果猴子補丁Artist.pick有以下幾點:

matplotlib.artist.Artist.orig_pick = matplotlib.artist.Artist.pick 
def nu_pick(self, me): 
    print self 
    matplotlib.artist.Artist.orig_pick(self, me) 

matplotlib.artist.Artist.pick = nu_pick 

你可以看一下的藝術家如何遞歸上挑的事件。 (每個Artist對象調用pick對它是自己的,然後對它的所有孩子)。由於我不明白的原因,在圖例的繪圖區域中每行有兩個副本(當它在內部和外部時,其行爲會有所不同)。

的一種方式 - 哈克解決方法就是算leglines有多少次被擊中,只有切換在奇數的:

import pylab 
import numpy 

# 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[0]) # 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: 
    line.set_picker(5) 

# Create a 2 way mapping between legend lines <-> plot lines  
line2leg = dict(zip(lines+leglines, leglines+lines)) 
count_dict = dict((l, 0) for l in lines) 
# Define event function 
def onpick(event): 
    thisline = event.artist 
    print event 
    print thisline 
    if thisline in lines: 
     print 'lines' 
     count_dict[thisline] = 0 
    elif thisline in leglines: 
     print 'leglines' 
     thisline = line2leg[thisline] 
     count_dict[thisline] += 1 
    print 'added' 
    if (count_dict[thisline] % 2) == 1: 
     print count_dict[thisline] 
     return 
    print 'tested' 
    if thisline.get_linestyle()==':': 
     print ": -> -" # For debugging 
     thisline.set_linestyle('-') 
     line2leg[thisline].set_linestyle('-') 
    else: 
     print "- -> :" # For debugging 
     thisline.set_linestyle(':') 
     line2leg[thisline].set_linestyle(':') 
    fig.canvas.draw() 

# connect event function  
fig.canvas.mpl_connect('pick_event', onpick) 
pylab.show() 

(我留在我所有的反竊聽語句)。

很確定這是一個錯誤,如果你不想在github上創建問題,我會的。

+0

謝謝,我會將問題保留一段時間,以查看是否有其他人可以添加對討論有用的任何內容 – Dhara 2013-04-30 10:54:05

+0

在github上創建[this](https://github.com/matplotlib/matplotlib/issues/1962)問題。 – Dhara 2013-04-30 11:01:38

3

我對傳奇藝術家的潛水發現,當圖例設置了bbox_to_anchor時,圖例的兩條線就位於圖例的子樹中。

我問了一個關於這個here與我的解決方案,我看了一個新的mouseevent,並跟蹤了我的回調已經處理過的藝術家。

我問意見,如果有人認爲有處理這種「功能」

我不知道這是一個錯誤更優雅的方式。但是,對於兒童行被保留在.lines屬性中並深入包裝盒數據結構的傳說來說,它似乎是獨一無二的 - get_children方法找到了這兩者。幸運的是,它們是同一個對象而不是副本,所以我可以檢查已經處理過的一行。

+0

我認爲這是圖例創建代碼中的一個錯誤。它只應該需要藝術家繪製它,並且當你設置'bbox_to_anchor'時,它不應該以這種方式改變行爲。 – tacaswell 2013-05-02 15:49:49

3

另一種類似tcaswell解決方案計數事件的方法。只有3行添加到原始legend picking example。它使用python的函數屬性。

def onpick(event): 
    if onpick.count % 2 == 0: #### LINE ADDED #### 
     # on the pick event, find the orig line corresponding to the 
     # legend proxy line, and toggle the visibility 
     legline = event.artist 
     origline = lined[legline] 
     vis = not origline.get_visible() 
     origline.set_visible(vis) 
     # Change the alpha on the line in the legend so we can see what lines 
     # have been toggled 
     if vis: 
      legline.set_alpha(1.0) 
     else: 
      legline.set_alpha(0.2) 
     fig.canvas.draw() 
    onpick.count += 1 #### LINE ADDED #### 

onpick.count = 0 #### LINE ADDED ####