在我们多线程编译并且使用PyQtGraph进行绘图时,我们需要确保所有的图形操作都在主线程中执行,主要是因为PyQtGraph是在主线程中创建的,并且不是线程安全的。下面我们将深入探讨在多线程环境下使用PyQtGraph绘图并做详细记录。
1、问题背景
在使用 PyQtGraph 绘图时,如果在主线程之外进行绘图操作,可能会出现绘图不生效或程序崩溃的问题。这是因为 PyQtGraph 的绘图操作需要在主线程中进行,否则可能会导致绘图操作与 GUI 界面更新操作冲突。
2、解决方案
为了解决这个问题,可以采用以下解决方案:
- 将绘图操作封装成一个函数,并将其放入一个队列中;
- 创建多个工作线程,并让每个工作线程从队列中获取绘图任务,然后在主线程中执行绘图操作;
- 通过信号和槽机制将工作线程和主线程连接起来,以便工作线程在完成绘图任务后通知主线程更新 GUI 界面。
以下是一个示例代码,演示了如何使用上述解决方案来在多线程环境下进行 PyQtGraph 绘图:
代码语言:javascript复制from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import threading
import Queue
# 创建一个绘图对象
app = QtWidgets.QApplication([])
win = pg.GraphicsWindow(title="Basic plotting examples")
plot = win.addPlot(title="Simple plot", labels={'left': 'Y Axis', 'bottom': 'X Axis'})
curve = plot.plot(curve=np.random.normal(size=100))
# 定义绘图函数
def draw(data):
curve.setData(data)
# 将绘图函数封装成一个类
class DrawingThread(threading.Thread):
def __init__(self, queue):
super(DrawingThread, self).__init__()
self.queue = queue
def run(self):
while True:
# 从队列中获取绘图任务
data = self.queue.get()
# 在主线程中执行绘图操作
QtCore.QMetaObject.invokeMethod(curve, "setData", QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(object, data))
# 通知主线程更新 GUI 界面
QtCore.QMetaObject.invokeMethod(app, "processEvents", QtCore.Qt.QueuedConnection)
# 标记任务完成
self.queue.task_done()
# 创建一个队列来存储绘图任务
queue = Queue.Queue()
# 创建多个工作线程
num_worker_threads = 3
for i in range(num_worker_threads):
thread = DrawingThread(queue)
thread.daemon = True
thread.start()
# 将绘图任务放入队列中
for i in range(100):
queue.put(np.random.normal(size=100))
# 启动应用程序
app.exec_()
在这个示例中,我们创建了一个绘图对象 curve
,并将其添加到 PyQtGraph 的绘图窗口中。然后,我们定义了一个绘图函数 draw()
,这个函数将数据 data
绘制到 curve
上。
接下来,我们将绘图函数封装成一个类 DrawingThread
。这个类继承自 threading.Thread
,并重写了 run()
方法。在 run()
方法中,我们从队列中获取绘图任务,然后在主线程中执行绘图操作。
最后,我们创建了一个队列 queue
来存储绘图任务,并创建了多个工作线程来从队列中获取绘图任务并执行绘图操作。
在主线程中,我们通过信号和槽机制将工作线程和主线程连接起来,以便工作线程在完成绘图任务后通知主线程更新 GUI 界面。
通过这种方式,我们可以在多线程环境下进行 PyQtGraph 绘图,并且不会出现绘图不生效或程序崩溃的问题。
最重要的需要注意的是,虽然数据生成过程是在工作线程中进行的,但数据传输和绘图更新操作都是在主线程中执行的,以确保PyQtGraph的线程安全性。如果有更多问题可以留言探讨。