标签:python event-handling matplotlib
我在使用matplotlib选择事件时遇到了一个非常奇怪的问题.我有两个艺术家,既可以选择也可以不重叠(“洞”和“钉子”).当我选择其中一个时,在事件处理期间,我将另一个移动到我刚刚点击的位置(将“挂钩”移动到“洞”中).然后,在没有做任何其他事情的情况下,即使在生成第一个事件时它不存在,也会生成来自移动的艺术家(peg)的拾取事件.我唯一的解释是,事件管理器在处理事件时仍会以某种方式移动艺术家图层,因此在第二个艺术家在光标下移动后会触及第二个艺术家.
那么我的问题是 – 挑选事件(或任何事件)如何迭代画布上的重叠艺术家,有没有办法控制它?如果它始终从上到下移动(而不是自下而上或随机),我想我会得到我想要的行为.我还没有找到足够的文档,对SO的冗长搜索没有透露这个确切的问题.下面是一个说明问题的工作示例,其中分散的PathCollections为pegs和holes:
import matplotlib.pyplot as plt
import sys
class peg_tester():
def __init__(self):
self.fig = plt.figure(figsize=(3,1))
self.ax = self.fig.add_axes([0,0,1,1])
self.ax.set_xlim([-0.5,2.5])
self.ax.set_ylim([-0.25,0.25])
self.ax.text(-0.4, 0.15, 'One click on the hole, and I get 2 events not 1',
fontsize=8)
self.holes = self.ax.scatter([1], [0], color='black', picker=0)
self.pegs = self.ax.scatter([0], [0], s=100, facecolor='#dd8800',
edgecolor='black', picker=0)
self.fig.canvas.mpl_connect('pick_event', self.handler)
plt.show()
def handler(self, event):
if event.artist is self.holes:
# If I get a hole event, then move a peg (to that hole) ...
# but then I get a peg event also with no extra clicks!
offs = self.pegs.get_offsets()
offs[0,:] = [1,0] # Moves left peg to the middle
self.pegs.set_offsets(offs)
self.fig.canvas.draw()
print 'picked a hole, moving left peg to center'
elif event.artist is self.pegs:
print 'picked a peg'
sys.stdout.flush() # Necessary when in ipython qtconsole
if __name__ == "__main__":
pt = peg_tester()
我已经尝试设置zorder以使栓钉始终位于孔洞之上,但这并不会改变拾取事件的生成方式,尤其是这个有趣的幻像事件.
编辑:
上下文是peg solitaire的一个实现,所以我希望能够拿起一个挂钩,然后点击一个空洞将其丢弃.目前,同一个挂钩一旦掉落就会立即再次拾起.
解决方法:
时间问题
你遇到的问题是由于pick_event函数的调用方式,matplotlib测试每个艺术家,如果它被选中,立即调用你的处理函数.测试顺序取决于您定义孔和钉的顺序(您可以通过更改钉和孔定义的顺序来验证它).
因此,避免此问题似乎很好的一种方法是使用matplotlib提供的计时器.在第一次有拾取事件时,您只需将艺术家添加到队列中,然后每x毫秒处理一次这个新数据.
以下是此类实现的示例:
import matplotlib.pyplot as plt
import sys
class peg_tester():
def __init__(self):
self.fig = plt.figure(figsize=(3,1))
self.ax = self.fig.add_axes([0,0,1,1])
self.ax.set_xlim([-0.5,2.5])
self.ax.set_ylim([-0.25,0.25])
self.ax.text(-0.4, 0.15,
'One click on the hole, and I get 2 events not 1',
fontsize=8)
self.holes = self.ax.scatter([1], [0], color='black', picker=0)
self.pegs = self.ax.scatter([0], [0], s=100, facecolor='#dd8800',
edgecolor='black', picker=0)
self.fig.canvas.mpl_connect('pick_event', self.handler)
# Creating a timer with a interval of 100 ms
self.timer=self.fig.canvas.new_timer(interval=100)
# Queue of pegs and holes that have been picked and waiting to be processed
self.list_pegs_holes=[]
self.timer.add_callback(self.update_pos,self.list_pegs_holes)
self.timer.start()
plt.show()
# Add the artist to the queue of artists awaiting to be processed
def handler(self, event):
self.list_pegs_holes.append(event.artist)
# Management of the queue
def update_pos(self,list_pegs_holes):
while len(list_pegs_holes)!=0:
artist=list_pegs_holes.pop(0)
if artist is self.holes:
# If I get a hole event, then move a peg (to that hole) ...
# but then I get a peg event also with no extra clicks!
offs = self.pegs.get_offsets()
offs[0,:] = [1,0] # Moves left peg to the middle
self.pegs.set_offsets(offs)
self.fig.canvas.draw()
print 'picked a hole, moving left peg to center'
elif artist is self.pegs:
print 'picked a peg'
sys.stdout.flush() # Necessary when in ipython qtconsole
if __name__ == "__main__":
pt = peg_tester()
大多数代码都是您提供的,我刚刚添加了计时器实现.
不是最佳的(过时的)
可以通过为您的艺术家定义特定的选择器来解决此问题.但是,您将无法在同一位置选择多个项目.
请参阅此示例,该示例解决了不应该选择钉子的部分:
– 通过以下方式替换钉子的定义:
self.pegs = self.ax.scatter([0], [0], s=100, facecolor='#dd8800',edgecolor='black', picker=self.pegs_picker)
– 添加函数pegs_picker:
def pegs_picker(figure,pegs,event):
# Check that the pointer is not on holes
if figure.holes.contains(event)[0]:
return False, dict()
else:
return True, dict()
只有当它们没有与孔重叠时,才能选择钉子.
我认为这可能是你想要的行为方式,但由于我不确切知道它是什么,我无法为你提供更精致的拣选功能.
标签:python,event-handling,matplotlib 来源: https://codeday.me/bug/20190624/1277693.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。