Python 如果图例位于轴之外,则在鼠标单击时注册双事件
我必须在一个绘图中绘制多个数据集。能够突出显示一个或多个图以进行比较非常有用。为此,每当直接选择一条线,或通过单击图例中的相应条目,我将在Python 如果图例位于轴之外,则在鼠标单击时注册双事件,python,python-2.7,matplotlib,Python,Python 2.7,Matplotlib,我必须在一个绘图中绘制多个数据集。能够突出显示一个或多个图以进行比较非常有用。为此,每当直接选择一条线,或通过单击图例中的相应条目,我将在“:”(背景图)和“-”(高亮显示图)之间切换绘图的线条样式 在我尝试使用bbox\u to\u锚定将图例移到轴外之前,这一切都非常有效。在此之后,在图例线上单击一次鼠标会连续触发两次单击事件,从而取消切换效果 如何将图例放置在轴外,同时保留拾取事件的正确行为 再现问题的简化代码(单击绘图线可在“高亮显示”和“未高亮显示”之间切换,而单击图例线可在返回前一状态
“:”
(背景图)和“-”
(高亮显示图)之间切换绘图的线条样式
在我尝试使用bbox\u to\u锚定
将图例移到轴外之前,这一切都非常有效。在此之后,在图例线上单击一次鼠标会连续触发两次单击事件,从而取消切换效果
如何将图例放置在轴外,同时保留拾取事件的正确行为
再现问题的简化代码(单击绘图线可在“高亮显示”和“未高亮显示”之间切换,而单击图例线可在返回前一状态之前短暂切换绘图线):
导入pylab
进口numpy
#创建用于打印的数据
t=numpy.linspace(0,1.0100)
a=numpy.sin(2*numpy.pi*t)
#设置数字
图=pylab.图()
ax=pylab.子批次(111)
#情节图
行=[]
对于范围(5)中的i:
line=ax.plot(t,(i+1)*a,linestyle=':',picker=5,label='line%d'(i+1))
行。追加(行[0])#保存打印行
#创建图例
支腿=轴。图例(bbox_至_锚=(1.01,1),loc=2)未按预期工作
#leg=ax.legend()#有效!!
#获取图例行
leglines=leg.get_lines()
#设置图例线的事件
对于腿部线条中的线条:
线。设置_选择器(5)
#在图例线和打印线之间创建双向映射
line2leg=dict(拉链(线+腿线,腿线+线))
#定义事件函数
def onpick(事件):
thisline=event.artist
如果thisline.get_linestyle()==':':
打印“:->-”#用于调试
thisline.set_线条样式('-'))
line2leg[thisline]。设置_线型('-'))
其他:
打印“-->:”#用于调试
thisline.set_linestyle(“:”)
line2leg[thisline]。设置线条样式(“:”)
图canvas.draw()
#连接事件函数
图canvas.mpl\u connect('pick\u event',onpick)
pylab.show()
如果你是猴子补丁艺术家。用以下选项选择:
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
)。出于我不理解的原因,图例的绘图区域中每一行都有两个副本(当它位于内部和外部时,其行为会有所不同)
黑客解决方案的一种方法是只计算腿线被击中的次数,只切换奇数:
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()
导入pylab
进口numpy
#创建用于打印的数据
t=numpy.linspace(0,1.0100)
a=numpy.sin(2*numpy.pi*t)
#设置数字
图=pylab.图()
ax=pylab.子批次(111)
#情节图
行=[]
对于范围(5)中的i:
line=ax.plot(t,(i+1)*a,linestyle=':',picker=5,label='line%d'(i+1))
行。追加(行[0])#保存打印行
#创建图例
支腿=轴。图例(bbox_至_锚=(1.01,1),loc=2)未按预期工作
#leg=ax.legend()#有效!!
#获取图例行
leglines=leg.get_lines()
#设置图例线的事件
对于腿部线条中的线条:
线。设置_选择器(5)
#在图例线和打印线之间创建双向映射
line2leg=dict(拉链(线+腿线,腿线+线))
count_dict=dict((l,0)表示行中的l)
#定义事件函数
def onpick(事件):
thisline=event.artist
打印事件
打印这行
如果此行在行中:
打印“行”
计数dict[thisline]=0
elif此行以leglines表示:
打印“leglines”
thisline=line2leg[thisline]
计数dict[thisline]+=1
打印“已添加”
如果(计数[thisline]%2)==1:
打印计数记录[此行]
返回
打印“已测试”
如果thisline.get_linestyle()==':':
打印“:->-”#用于调试
thisline.set_线条样式('-'))
line2leg[thisline]。设置_线型('-'))
其他:
打印“-->:”#用于调试
thisline.set_linestyle(“:”)
line2leg[thisline]。设置线条样式(“:”)
图canvas.draw()
#连接事件函数
图canvas.mpl\u connect('pick\u event',onpick)
pylab.show()
(我把我所有的去窃听声明都留在了里面)
非常确定这是一个bug,如果你不想在github上创建问题,我会的。我深入到传奇艺术家中发现,当传奇设置了bbox_to_锚定时,传奇的子树中有两条传奇线
我用我的解决方案询问了这个问题,在那里我观察了一个新的mouseevent,并跟踪了已经被我的回调处理过的艺术家
如果有人认为有一种更优雅的方式来处理这个“功能”,我会征求意见
我不确定这是一个错误。但是,在图例中,子行保存在.lines属性中,并且深入到packing box数据结构中,这似乎是独一无二的—get_children方法可以找到这两个。幸运的是,它们是相同的对象,而不是副本,因此我可以检查是否有一行已经处理。另一种类似于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 ####
这很奇怪。看起来同一个pick事件被处理了两次,而不是生成了两个事件,这让我觉得这是一个bug@tcaswell如何判断它是相同的拾取事件还是生成了两个相同类型的事件?如果在onpick
中添加print event
行,则它们在打印时具有相同的内存地址,我会让这个问题继续讨论一段时间,看看是否有其他人可以为github上的discussionCreated问题添加有用的内容。我认为这是legend创建代码中的一个bug。它应该只需要艺术家画一次就可以了
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 ####