这个问题与以下两个问题密切相关,但是这个问题更为笼统。

Matplotlib pick event order for overlapping artists

Multiple pick events interfering



问题:

在单个画布上拾取重叠的艺术家时,将为每个艺术家创建单独的拾取事件。在下面的示例中,单击一个红色点会两次调用on_pick,一次是lines,一次是points。由于points位于该行的上方(根据它们各自的zorder值),所以我希望只为最顶级的艺术家(在这种情况下为points)生成一个单独的选择事件。

例:

python - 在Matplotlib中从一组重叠的艺术家中挑选一个艺术家-LMLPHP

import numpy as np
from matplotlib import pyplot

def on_pick(event):
    if event.artist == line:
        print('Line picked')
    elif event.artist == points:
        print('Point picked')


# create axes:
pyplot.close('all')
ax      = pyplot.axes()

# add line:
x       = np.arange(10)
y       = np.random.randn(10)
line    = ax.plot(x, y, 'b-', zorder=0)[0]

# add points overlapping the line:
xpoints = [2, 4, 7]
points  = ax.plot(x[xpoints], y[xpoints], 'ro', zorder=1)[0]

# set pickers:
line.set_picker(5)
points.set_picker(5)
ax.figure.canvas.mpl_connect('pick_event', on_pick)

pyplot.show()




凌乱的解决方案:

一种解决方案是使用Matplotlib的button_press_event,然后计算鼠标与所有艺术家之间的距离,如下所示。但是,此解决方案非常混乱,因为添加其他重叠的美术师会使此代码变得相当复杂,从而增加了要检查的案例和条件的数量。

def on_press(event):
    if event.xdata is not None:
        x,y   = event.xdata, event.ydata  #mouse click coordinates
        lx,ly = line.get_xdata(), line.get_ydata()     #line point coordinates
        px,py = points.get_xdata(), points.get_ydata() #points
        dl    = np.sqrt((x - lx)**2 + (y - ly)**2)     #distances to line points
        dp    = np.sqrt((x - px)**2 + (y - py)**2)     #distances to points
        if dp.min() < 0.05:
            print('Point selected')
        elif dl.min() < 0.05:
            print('Line selected')


pyplot.close('all')
ax      = pyplot.axes()

# add line:
x       = np.arange(10)
y       = np.random.randn(10)
line    = ax.plot(x, y, 'b-', zorder=0)[0]

# add points overlapping the line:
xpoints = [2, 4, 7]
points  = ax.plot(x[xpoints], y[xpoints], 'ro', zorder=1)[0]

# set picker:
ax.figure.canvas.mpl_connect('button_press_event', on_press)

pyplot.show()




问题摘要:
是否有更好的方法从一组重叠的艺术家中选择最高的艺术家?

理想情况下,我希望能够执行以下操作:

pyplot.set_pick_stack( [points, line] )


表示将为重叠的选择在points上选择line

最佳答案

button_press_event发生时创建自己的事件可能是最简单的。为了说明问题中表达的“ set_pick_stack”的概念,可能如下所示。这个想法是存储一组艺术家,并在button_press_event中检查该事件是否包含在艺术家中。然后在自定义onpick函数上触发回调。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backend_bases import PickEvent

class PickStack():
    def __init__(self, stack, on_pick):
        self.stack = stack
        self.ax = [artist.axes for artist in self.stack][0]
        self.on_pick = on_pick
        self.cid = self.ax.figure.canvas.mpl_connect('button_press_event',
                                                     self.fire_pick_event)

    def fire_pick_event(self, event):
        if not event.inaxes:
            return
        cont = [a for a in self.stack if a.contains(event)[0]]
        if not cont:
            return
        pick_event = PickEvent("pick_Event", self.ax.figure.canvas,
                               event, cont[0],
                               guiEvent=event.guiEvent,
                               **cont[0].contains(event)[1])
        self.on_pick(pick_event)


用法看起来像

fig, ax = plt.subplots()

# add line:
x       = np.arange(10)
y       = np.random.randn(10)
line,   = ax.plot(x, y, 'b-', label="Line", picker=5)

# add points overlapping the line:
xpoints = [2, 4, 7]
points,  = ax.plot(x[xpoints], y[xpoints], 'ro', label="Points", picker=5)


def onpick(event):
    txt = f"You picked {event.artist} at xy: " + \
          f"{event.mouseevent.xdata:.2f},{event.mouseevent.xdata:.2f}" + \
          f" index: {event.ind}"
    print(txt)

p = PickStack([points, line], onpick)

plt.show()


这里的想法是按挑选事件所需的顺序提供艺术家列表。当然,也可以使用zorder确定顺序。这看起来像

self.stack = list(stack).sort(key=lambda x: x.get_zorder(), reverse=True)


__init__函数中。

因为问题引起了评论,所以让我们看看为什么matplotlib不自动执行此过滤。好吧,首先,我想至少在50%的情况下都是不希望的,在这种情况下,您希望每个挑选的艺术家都参加一次活动。而且,对于matplotlib而言,为每个受mouseevent击中的艺术家发出事件比过滤它们要容易得多。对于前者,您只需比较坐标(非常类似于问题中的“混乱解决方案”)。很难只获得最高的艺术家;当然,如果两个艺术家的zorder不同,这是有可能的,但是如果他们具有相同的zorder,则只是他们在子代轴列表中出现的顺序决定了哪个在前。 “ pick_upmost_event”将需要检查完整的轴子代堆栈,以找出要选择的轴。话虽如此,这并不是没有可能,但是到目前为止,可能还没有人相信这是值得的。当然,人们可以针对这样的“ pick_upmost_event”打开问题或将实施提交为matplotlib的PR。

关于python - 在Matplotlib中从一组重叠的艺术家中挑选一个艺术家,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56015753/

10-14 00:56