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

2024-04-28 22:06:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我在matplotlib pick事件中遇到了一个非常奇怪的问题。我有两个艺术家,都是可挑选的,不重叠开始(“洞”和“钉”)。当我选择其中一个时,在事件处理过程中,我将另一个移到我刚才单击的位置(将一个“peg”移到“hole”中)。然后,不做任何其他操作,从移动的艺术家(peg)生成一个pick事件,即使在生成第一个事件时,它并不在那里。我对它的唯一解释是,事件管理器在处理事件时仍然在艺术家层中移动,因此在它移动到光标下之后,它会击中第二个艺术家。在

所以我的问题是——如何在画布上迭代pick events(或任何事件)迭代重叠的艺术家,有没有方法控制它?我想如果它总是自上而下(而不是自下而上或随机地)移动,我会得到我想要的行为。我还没能找到足够的文档,经过长时间的搜索,没有发现这个确切的问题。下面是一个说明问题的工作示例,其中PathCollections来自scatter作为桩和孔:

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事件的生成方式,尤其是这个有趣的幻影事件。在

编辑: 上下文是一个peg纸牌的实现,所以我想能够拿起一个peg,然后单击一个空洞将它放在那里。目前,同一钉住汇率制度一旦被撤销,就会立即被再次提起。在


Tags: toselfeventgetfig事件pltax
2条回答

时间问题

您遇到的问题是由于pick_event函数的调用方式造成的,matplotlib测试每个艺术家,如果它是立即选择的,则调用handler函数。测试顺序取决于定义孔和桩的顺序(可以通过更改桩和孔的定义顺序来验证)。在

因此,避免这个问题的一个好方法是使用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()

大部分代码都是您提供的,我只是添加了计时器实现。在

非最佳(过时)

可以通过为艺术家定义特定的选取器来修复此行为。但是你不能在同一地点选择几个项目。在

请看下面这个例子,该示例解决了不应拾取桩的部分:

-将钉的定义替换为:

^{pr2}$

-添加函数pegs\u 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()

有了那个钉子,只有当它们没有和洞重叠的时候才是可挑选的。在

我认为这可能是获得你想要的行为的方式,但是由于我不知道它到底是什么,我无法为您提供更精细的选择函数。在

相关问题 更多 >