Matplotlib选择重叠艺术家的事件顺序



我遇到了一个非常奇怪的问题与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()

只有当它们不与洞叠加在一起时,它们才可以被拾取。

我认为这可能是你想要的行为,但因为我不知道它到底是什么,我不能提供更精细的选择函数。

最新更新