在 PyQt5 窗口中嵌入交互式 Matplotlib 3D 图表时,关键细节在于创建画布和 3D 坐标轴的顺序。
核心技巧是在添加 3D 坐标轴之前先创建 FigureCanvas。这样可以保留鼠标交互所需的事件连接。
相关的 Stack Overflow 笔记:
最小模式
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111, projection='3d')
或者,将其封装在自定义画布类中:
class MyFigureCanvas(FigureCanvas):
def __init__(self):
self.figure = Figure()
super(FigureCanvas, self).__init__(self.figure)
self.axes = self.figure.add_subplot(111, projection='3d')
如果鼠标旋转、平移或缩放仍然不起作用,可以在画布创建完成后调用 ax.mouse_init():
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111, projection='3d')
ax.mouse_init()
完整 PyQt5 示例
import sys
import numpy as np
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D # noqa: F401; registers the 3D projection
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Interactive 3D Matplotlib Plot')
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111, projection='3d')
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x ** 2 + y ** 2))
ax.plot_surface(x, y, z, cmap='viridis')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.mouse_init()
container = QWidget()
layout = QVBoxLayout(container)
layout.addWidget(canvas)
self.setCentralWidget(container)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.resize(900, 700)
window.show()
sys.exit(app.exec_())
图表应该可以用鼠标左键旋转,并可通过滚轮缩放。如果交互失败,首先检查画布是否在 add_subplot(..., projection='3d') 之前创建,然后用下面的代码确认已安装的 Matplotlib 后端:
import matplotlib
print(matplotlib.get_backend())
对于 PyQt5,后端通常应为 Qt5Agg 或其他与 Qt 兼容的后端。
