3

我必须在一个图中绘制多个数据集。能够突出显示一个或多个图以便比较它们是很有用的。为此,只要直接选择一条线,或者单击图例中的相应条目,我就会在":"(背景图)和(突出显示的图)之间切换绘图的线型。"-"

这非常有效,直到我尝试使用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()
4

3 回答 3

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 上创建问题,我会的。

于 2013-04-30T02:55:51.653 回答
3

我深入传奇艺术家发现,当图例设置了 bbox_to_anchor 时,图例行在图例的子树中两次。

我在这里用我的解决方案询问了这个问题,我观察了一个新的鼠标事件并跟踪已经由我的回调处理的艺术家。

如果有人认为有更优雅的方式来处理这个“功能”,我已经征求意见

我不确定这是一个错误。但是,对于子行保存在 .lines 属性中并深入包装盒数据结构的传说来说,这似乎是独一无二的——get_children 方法可以找到这两者。幸运的是,它们是同一个对象而不是副本,因此我可以检查是否有已处理的行。

于 2013-05-02T00:15:48.167 回答
3

另一种类似于 tcaswell 的解决方案计数事件的方法。仅在原始图例选取示例中添加了 3 行。它使用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 ####
于 2014-01-25T17:31:42.737 回答