选择事件演示#

你可以通过设置艺术家的 “picker” 属性来启用选择功能(例如 Matplotlib 中的 Line2D、Text、Patch、Polygon、AxesImage 等)。

picker 属性有多种含义:

  • None - 对于此艺术家禁用选择(默认)

  • bool - 如果为 True,则启用选择功能,当鼠标事件在艺术家上时,艺术家将触发 pick 事件。 设置 pickradius 将在点中添加 epsilon 容差,如果其数据在鼠标事件的 epsilon 范围内,则艺术家将触发事件。对于某些艺术家如线条和补丁集合,艺术家可能会为生成的 pick 事件提供额外的数据,例如在 pick 事件的 epsilon 范围内的数据的索引。

  • function - 如果 picker 是可调用的,它是用户提供的函数,用于确定鼠标事件是否命中艺术家。

    hit, props = picker(artist, mouseevent)
    

    来确定命中测试。如果鼠标事件在艺术家上,返回 hit=True 并且 props 是要添加到 PickEvent 属性的字典。

在通过设置 "picker" 属性启用艺术家的选择后,你需要连接到 figure canvas 的 pick_event,以在鼠标按下事件上获得 pick 回调。例如,:: python     def pick_handler(event):         mouseevent = event.mouseevent         artist = event.artist         # 现在用这个做一些事情...    

传递给你的回调的 pick 事件(matplotlib.backend_bases.PickEvent)总是带有两个属性:

  • mouseevent 生成 pick 事件的鼠标事件。

    鼠标事件又具有诸如 xy(显示空间中的坐标,例如从左下角的像素)以及 xdata, ydata(数据空间中的坐标)等属性。此外,你可以获取有关哪个按钮被按下、哪些键被按下、鼠标在哪个轴上等信息。请参阅 matplotlib.backend_bases.MouseEvent 获取详细信息。

  • artist 生成 pick 事件的 matplotlib.artist

另外,像 Line2D 和 PatchCollection 这样的特定艺术家可能会附加额外的元数据,如满足选择标准的数据的索引(例如,在指定的 epsilon 容差范围内的所有点)。

以下示例说明了这些方法。

# %matplotlib tk
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import rand

from matplotlib.image import AxesImage
from matplotlib.lines import Line2D
from matplotlib.patches import Rectangle
from matplotlib.text import Text

# Fixing random state for reproducibility
np.random.seed(19680801)

简单的选择:线条、矩形和文本#

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.set_title('click on points, rectangles or text', picker=True)
ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red'))
line, = ax1.plot(rand(100), 'o', picker=True, pickradius=5)

# Pick the rectangle.
ax2.bar(range(10), rand(10), picker=True)
for label in ax2.get_xticklabels():  # Make the xtick labels pickable.
    label.set_picker(True)


def onpick1(event):
    if isinstance(event.artist, Line2D):
        thisline = event.artist
        xdata = thisline.get_xdata()
        ydata = thisline.get_ydata()
        ind = event.ind
        print('onpick1 line:', np.column_stack([xdata[ind], ydata[ind]]))
    elif isinstance(event.artist, Rectangle):
        patch = event.artist
        print('onpick1 patch:', patch.get_path())
    elif isinstance(event.artist, Text):
        text = event.artist
        print('onpick1 text:', text.get_text())


fig.canvas.mpl_connect('pick_event', onpick1)
14
onpick1 line: [[37.          0.48320961]]
onpick1 line: [[35.          0.78306588]]
onpick1 patch: Path(array([[0., 0.],
       [1., 0.],
       [1., 1.],
       [0., 1.],
       [0., 0.]]), array([ 1,  2,  2,  2, 79], dtype=uint8))
onpick1 patch: Path(array([[0., 0.],
       [1., 0.],
       [1., 1.],
       [0., 1.],
       [0., 0.]]), array([ 1,  2,  2,  2, 79], dtype=uint8))
onpick1 patch: Path(array([[0., 0.],
       [1., 0.],
       [1., 1.],
       [0., 1.],
       [0., 0.]]), array([ 1,  2,  2,  2, 79], dtype=uint8))
onpick1 line: [[82.         0.2268104]]
onpick1 line: [[82.         0.2268104]]
onpick1 line: [[82.         0.2268104]]
onpick1 line: [[82.         0.2268104]]
onpick1 patch: Path(array([[0., 0.],
       [1., 0.],
       [1., 1.],
       [0., 1.],
       [0., 0.]]), array([ 1,  2,  2,  2, 79], dtype=uint8))

使用自定义命中测试函数的选择#

你可以通过将 picker 设置为可调用的函数来定义自定义选择器。该函数具有以下签名:

hit, props = func(artist, mouseevent)

来确定命中测试。如果鼠标事件在艺术家上,返回 hit=True 并且 props 是你想添加到 .PickEvent 属性的属性字典。

def line_picker(line, mouseevent):
    """
    Find the points within a certain distance from the mouseclick in
    data coords and attach some extra attributes, pickx and picky
    which are the data points that were picked.
    """
    if mouseevent.xdata is None:
        return False, dict()
    xdata = line.get_xdata()
    ydata = line.get_ydata()
    maxd = 0.05
    d = np.sqrt(
        (xdata - mouseevent.xdata)**2 + (ydata - mouseevent.ydata)**2)

    ind, = np.nonzero(d <= maxd)
    if len(ind):
        pickx = xdata[ind]
        picky = ydata[ind]
        props = dict(ind=ind, pickx=pickx, picky=picky)
        return True, props
    else:
        return False, dict()


def onpick2(event):
    print('onpick2 line:', event.pickx, event.picky)


fig, ax = plt.subplots()
ax.set_title('custom picker for line data')
line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker)
fig.canvas.mpl_connect('pick_event', onpick2)
14
onpick2 line: [0.11632148] [0.68948604]
onpick2 line: [0.06097709 0.05552486] [0.79304089 0.84058217]
onpick2 line: [0.53413535] [0.45664264]
onpick2 line: [0.51195743 0.53413535] [0.50131575 0.45664264]
onpick2 line: [0.51195743 0.53413535] [0.50131575 0.45664264]
onpick2 line: [0.5838648] [0.53457107]
onpick2 line: [0.70743918] [0.55113251]
onpick2 line: [0.15024598 0.19481606 0.22140904 0.20272247] [0.28792293 0.26229789 0.24974709 0.2895639 ]

在散点图上的选择#

散点图由 PathCollection 支持。

x, y, c, s = rand(4, 100)


def onpick3(event):
    ind = event.ind
    print('onpick3 scatter:', ind, x[ind], y[ind])


fig, ax = plt.subplots()
ax.scatter(x, y, 100*s, c, picker=True)
fig.canvas.mpl_connect('pick_event', onpick3)
14
onpick3 scatter: [70 97] [0.24263777 0.23168902] [0.70451921 0.68434196]

选择图像#

使用 .Axes.imshow 绘制的图像是 AxesImage 对象。

fig, ax = plt.subplots()
ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True)
ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True)
ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True)
ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True)
ax.set(xlim=(0, 5), ylim=(0, 5))


def onpick4(event):
    artist = event.artist
    if isinstance(artist, AxesImage):
        im = artist
        A = im.get_array()
        print('onpick4 image', A.shape)


fig.canvas.mpl_connect('pick_event', onpick4)

plt.show()
onpick4 image (10, 5)
onpick4 image (20, 25)
onpick4 image (30, 12)
onpick4 image (5, 10)

选择事件演示 2#

计算 100 个数据集的均值(mu)和标准差(sigma),绘制 mu 与 sigma。当你点击其中一个 (mu, sigma) 点时,绘制生成该点的数据集的原始数据。

import matplotlib.pyplot as plt
import numpy as np

# Fixing random state for reproducibility
np.random.seed(19680801)

X = np.random.rand(100, 1000)
xs = np.mean(X, axis=1)
ys = np.std(X, axis=1)

fig, ax = plt.subplots()
ax.set_title('click on point to plot time series')
line, = ax.plot(xs, ys, 'o', picker=True, pickradius=5)


def onpick(event):

    if event.artist != line:
        return

    N = len(event.ind)
    if not N:
        return

    figi, axs = plt.subplots(N, squeeze=False)
    for ax, dataind in zip(axs.flat, event.ind):
        ax.plot(X[dataind])
        ax.text(.05, .9, f'mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}',
                transform=ax.transAxes, va='top')
        ax.set_ylim(-0.5, 1.5)
    figi.show()


fig.canvas.mpl_connect('pick_event', onpick)

plt.show()