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
相关产品推荐
相关产品推荐

