在使用 Matplotlib 进行实时绘图时,如果在其他线程中访问 Matplotlib 的绘图组件,可能会导致程序崩溃。这是因为 Matplotlib 在 Qt 后端下,绘图操作必须在主线程中进行,而在其他线程中进行绘图操作会引发竞争条件,导致程序崩溃。
2. 解决方案
解决这个问题的常见方法有两种:
- 方法一:使用定时器
可以使用定时器在主线程中定期调用绘图函数,从而避免在其他线程中访问 Matplotlib 组件。这种方法简单易行,但可能会导致绘图延迟。
- 方法二:使用事件
可以使用 QApplication.postEvent() 方法将绘图事件发送到主线程,从而在主线程中进行绘图操作。这种方法可以避免绘图延迟,但可能需要对代码进行一些修改。
代码例子
方法一:使用定时器
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import matplotlib.pyplot as plt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# 创建 Matplotlib 窗口
self.mpl_widget = MplWidget()
self.setCentralWidget(self.mpl_widget)
# 创建定时器
self.timer = QtCore.QTimer()
self.timer.setInterval(100) # 100 毫秒
self.timer.timeout.connect(self.update_plot)
# 启动定时器
self.timer.start()
def update_plot(self):
# 在主线程中更新绘图
self.mpl_widget.update_plot()
class MplWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# 创建 Matplotlib 图形
self.figure, self.axes = plt.subplots()
# 将 Matplotlib 图形嵌入到 QWidget 中
self.canvas = FigureCanvas(self.figure)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def update_plot(self):
# 更新绘图
self.axes.cla() # 清除绘图区域
x = [1, 2, 3, 4, 5]
y = [10, 20, 30, 40, 50]
self.axes.plot(x, y)
self.canvas.draw() # 绘制图形
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
方法二:使用事件
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import matplotlib.pyplot as plt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# 创建 Matplotlib 窗口
self.mpl_widget = MplWidget()
self.setCentralWidget(self.mpl_widget)
# 创建事件循环
self.event_loop = QtCore.QEventLoop()
# 创建定时器
self.timer = QtCore.QTimer()
self.timer.setInterval(100) # 100 毫秒
self.timer.timeout.connect(self.update_plot)
# 启动定时器
self.timer.start()
# 启动事件循环
self.event_loop.exec_()
def update_plot(self):
# 在主线程中更新绘图
QtWidgets.QApplication.postEvent(self.mpl_widget, PlotEvent())
class PlotEvent(QtCore.QEvent):
def __init__(self):
super().__init__(QtCore.QEvent.User)
class MplWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# 创建 Matplotlib 图形
self.figure, self.axes = plt.subplots()
# 将 Matplotlib 图形嵌入到 QWidget 中
self.canvas = FigureCanvas(self.figure)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def event(self, event):
if isinstance(event, PlotEvent):
# 更新绘图
self.axes.cla() # 清除绘图区域
x = [1, 2, 3, 4, 5]
y = [10, 20, 30, 40, 50]
self.axes.plot(x, y)
self.canvas.draw() # 绘制图形
return True
else:
return super().event(event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()