我遇到了一个非常奇怪的问题与matplotlib挑选事件。我有两个美术师,都是可选择的,并且没有重叠("洞"one_answers"钉")。当我选择其中一个时,在事件处理期间,我将移动另一个到我刚刚点击的位置(将"peg"移动到"hole"中)。然后,无需做任何其他事情,从移动的艺术家(peg)生成一个拾取事件,即使在生成第一个事件时它并不存在。我唯一的解释是,当事件被处理时,事件管理器仍然在艺术家层之间移动,因此在移动到光标下后,它会击中第二个艺术家。
那么我的问题是-如何选择事件(或任何事件)迭代重叠的艺术家在画布上,有没有一种方法来控制它?我认为如果它总是从上到下(而不是从下到上或随机)移动,我会得到我想要的行为。我没能找到足够的文档,在SO上进行了冗长的搜索,并没有发现这个确切的问题。下面是一个说明问题的工作示例,scatter
中的PathCollections
作为钉和孔:
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一旦被丢弃,就会立即再次被捡起。
计时问题
您遇到的问题是由于调用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()
只有当它们不与洞叠加在一起时,它们才可以被拾取。
我认为这可能是你想要的行为,但因为我不知道它到底是什么,我不能提供更精细的选择函数。