You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib .contains方法环境更新后失效原因咨询

Matplotlib scatter.contains()方法环境更新后失效的原因分析

问题背景

在matplotlib 3.4.3 + Python 3.8.19环境中,sc.contains(event)方法可正常返回布尔值和包含数据点索引的字典;更新环境后,即使鼠标悬停在数据点上,该方法始终返回False和空字典。回退至原环境后确认问题由环境更新导致,已找到标注替代方案,但需明确失效原因。

最小可复现代码

import sys
import numpy
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Live Charts")
        self.statusbar = self.statusBar()
        self.setCentralWidget(GraphWidget())

class GraphWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.prev_ind = None
        self.canvas = FigureCanvas()
        vertical_layout = QVBoxLayout(self)
        vertical_layout.addWidget(self.canvas)

        self.fig = plt.figure(figsize=(4, 4), dpi=100)
        self.fig.set_tight_layout(True)
        self.ax = self.fig.add_subplot()
        self.canvas.figure = self.fig

        x = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
        y = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9])

        self.sc = self.ax.scatter(x, y, edgecolors='Midnightblue', color='cornflowerblue')

        self.annot = self.ax.annotate("", (x, y), annotation_clip=True, size=7, 
                                      textcoords="offset points",
                                      bbox=dict(boxstyle="round", fc="w"),
                                      arrowprops=dict(arrowstyle="->"))
        self.annot.set_visible(False)
        self.fig.canvas.mpl_connect("motion_notify_event", self.hover)
        self.canvas.draw()

    def hover(self, event):
        vis = self.annot.get_visible()
        if event.inaxes == self.ax:
            print("event: " + str(event))
            self.cont, self.ind = self.sc.contains(event)
            print("self.ind: " + str(self.ind))
            print("self.cont: " + str(self.cont))
            if self.cont:
                self.update_annot(self.ind)
                self.annot.set_visible(True)
                self.prev_ind = self.ind
                self.canvas.draw()
            else:
                if vis:
                    self.annot.set_visible(False)
                    self.prev_ind = None
                    self.canvas.draw()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.showMaximized()
    window.activateWindow()
    sys.exit(app.exec())

失效原因分析

1. FigureCanvas与Figure的绑定方式错误

代码中先创建空的FigureCanvas()实例,再通过self.canvas.figure = self.fig手动绑定figure对象。这种写法在Matplotlib 3.4.x中存在兼容性,但在3.5+版本中,canvas的事件系统需要在初始化阶段就与figure建立关联,否则无法正确传递鼠标事件的坐标信息,导致contains()方法无法正确检测鼠标是否命中数据点。

正确的初始化方式应为:

self.fig = plt.figure(figsize=(4, 4), dpi=100)
self.canvas = FigureCanvas(self.fig)  # 初始化时直接传入figure

2. Matplotlib 3.5+版本的拾取逻辑变更

从Matplotlib 3.5开始,PathCollection.contains()(scatter方法返回的对象类型)的拾取检测逻辑做了调整:

  • 默认情况下,若未显式设置picker参数,拾取检测的触发条件变得更严格,可能无法识别鼠标悬停;
  • 方法对事件坐标的解析依赖canvas与figure的正确绑定,错误的绑定会导致坐标传递失败,直接返回False和空字典。

3. 额外优化建议

显式设置scatter的picker参数(如picker=5,表示拾取半径为5像素),可以确保拾取检测的可靠性,避免因默认参数变化导致的问题:

self.sc = self.ax.scatter(x, y, edgecolors='Midnightblue', color='cornflowerblue', picker=5)

内容的提问来源于stack exchange,提问作者Denver_Colorado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:56:12