如果图例在轴外,则在鼠标单击时注册双事件 [英] Double event registered on mouse-click if legend is outside axes

查看:39
本文介绍了如果图例在轴外,则在鼠标单击时注册双事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须在一个图中绘制多个数据集.能够突出显示一个或多个图表以进行比较是很有用的.为此,无论何时直接选择一条线,我都会在:" (背景图)和-" (突出显示的图)之间切换图的线型,或者通过单击图例中的相应条目.

I have to plot several data sets in one plot. It is useful to be able to highlight one or more of the plots in order to compare them. For this, I toggle the line style of the plot between ":" (background plot) and "-" (highlighted plot) whenever a line is selected directly, or, by clicking on the corresponding entry in the legend.

这非常有效,直到我尝试使用 bbox_to_anchor 将图例移到轴外.之后,在图例行上单击鼠标即可连续触发2次单击事件,从而取消了切换效果.

This works perfectly until I try to move the legend outside the axes using bbox_to_anchor. After this, a single mouse click on the legend line triggers 2 click events in succession, thereby canceling the toggling effect.

如何在保持 pick_event 的正确行为的同时将图例放置在轴外?

How do I place the legend outside the axes while preserving the correct behaviour for the pick_event?

重现该问题的简化代码(单击绘图线可在突出显示"和未突出显示"之间切换,而单击图例行可在返回到之前的状态之前短暂切换绘图线):

Simplified code that reproduces the problem (Clicking on a plot line toggles between 'highlighted' and 'not-highlighted', whereas clicking on a legend line briefly toggles the plot-line before going back to the previous state):

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()

推荐答案

如果您使用以下内容修改 Artist.pick:

If you monkey patch Artist.pick with the following:

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 ).由于我不明白的原因,图例的绘图区域中每条线都有两个副本(并且在内部和外部时表现不同).

You can look at how the artists recurse on a pick event. (Each Artist object calls pick on it's self and then on all of it's children). For reasons I don't understand, there are two copies of each line in the drawing area of the legend (and it behaves differently when it is inside and outside).

一个简单的解决方案是只计算 leglines 被击中的次数,并且只切换奇数:

A way-hacky solution is to just count how many times the leglines have been hit, and only toggle on the odd ones:

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()

(我把所有的调试语句都留在了里面).

(I left all my de-bugging statements in).

如果您不想在github上创建问题,请确保这是一个错误.

Pretty sure this is a bug, if you don't want to create an issue on github I will.

这篇关于如果图例在轴外,则在鼠标单击时注册双事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆