【问题标题】:How to have a fast crosshair mouse cursor for subplots in matplotlib?如何在 matplotlib 中为子图设置快速十字光标?
【发布时间】:2025-11-23 03:10:01
【问题描述】:

backtradermatplotlib 实现https://youtu.be/m6b4Ti4P2HA?t=2008 的这段视频中,我可以看到matplotlib 中似乎存在一个默认且非常快速且节省CPU 的十字准线鼠标光标。

我想在matplotlib 中为一个简单的多子图绘图使用相同类型的鼠标光标,如下所示:

import numpy as np
import matplotlib

matplotlib.use('QT5Agg')
matplotlib.rcParams['figure.figsize'] = (20.0, 22.0)
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)

ax1.plot(np.array(np.random.rand(100)))
ax2.plot(np.array(np.random.rand(100)))

plt.show()

所以,如果我在下图中使用鼠标,我想直接且非常准确地查看下图中的哪个 x/y 值对应于上图中的哪个值对。

我找到了其他解决方案,但与视频中的实现相比,它们似乎非常慢。

【问题讨论】:

  • 您的问题还没有解决吗?

标签: python-3.x matplotlib mplcursors


【解决方案1】:

您可以通过mplcursors 创建十字光标。 sel.extras.append() 负责在绘制新光标时删除旧光标。使用sel.annotation.set_text,您可以调整显示的弹出注释。要省略注释,请使用sel.annotation.set_visible(False)。要在另一个子图中找到对应的 y 值,可以使用 np.interp 以及从曲线中提取的数据。

import numpy as np
import matplotlib.pyplot as plt
import mplcursors

def crosshair(sel):
    x, y2 = sel.target
    y1 = np.interp( sel.target[0],   plot1.get_xdata(), plot1.get_ydata() )
    sel.annotation.set_text(f'x: {x:.2f}\ny1: {y1:.2f}\ny2: {y2:.2f}')
    # sel.annotation.set_visible(False)
    hline1 = ax1.axhline(y1, color='k', ls=':')
    vline1 = ax1.axvline(x, color='k', ls=':')
    vline2 = ax2.axvline(x, color='k', ls=':')
    hline2 = ax2.axhline(y2, color='k', ls=':')
    sel.extras.append(hline1)
    sel.extras.append(vline1)
    sel.extras.append(hline2)
    sel.extras.append(vline2)

fig = plt.figure(figsize=(15, 10))
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)

plot1, = ax1.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
plot2, = ax2.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))

cursor = mplcursors.cursor(plot2, hover=True)
cursor.connect('add', crosshair)

plt.show()

这是一个替代实现,它将数据存储在全局变量中并移动行(而不是删除和重新创建它们):

import numpy as np
import matplotlib.pyplot as plt
import mplcursors

def crosshair(sel):
    x = sel.target[0]
    y1 = np.interp(x, plot1x, plot1y)
    y2 = np.interp(x, plot2x, plot2y)
    sel.annotation.set_visible(False)
    hline1.set_ydata([y1])
    vline1.set_xdata([x])
    hline2.set_ydata([y2])
    vline2.set_xdata([x])
    hline1.set_visible(True)
    vline1.set_visible(True)
    hline2.set_visible(True)
    vline2.set_visible(True)

fig = plt.figure(figsize=(15, 10))
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)

plot1, = ax1.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
plot2, = ax2.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))

plot1x = plot1.get_xdata()
plot1y = plot1.get_ydata()
plot2x = plot2.get_xdata()
plot2y = plot2.get_ydata()
hline1 = ax1.axhline(plot1y[0], color='k', ls=':', visible=False)
vline1 = ax1.axvline(plot1x[0], color='k', ls=':', visible=False)
hline2 = ax2.axhline(plot2y[0], color='k', ls=':', visible=False)
vline2 = ax2.axvline(plot2x[0], color='k', ls=':', visible=False)

cursor = mplcursors.cursor([plot1, plot2], hover=True)
cursor.connect('add', crosshair)

plt.show()

【讨论】:

  • 感谢您的努力。但与 Backtrader 相比,这个解决方案对我来说似乎不是很快
  • 你确定吗?关闭注释,似乎与视频中显示的速度相同。
  • 为了加快速度,您可以将plot1.get_xdata()plot1.get_ydata() 的结果存储在全局变量中。你无法避免使用 matplotlib 功能。
  • 我现在意识到 Backtrader 使用了 matplotlib 的 MultiCursor() 功能(虽然稍作修改)。我认为这应该是适当的解决方案。
最近更新 更多