在嵌入式PyQt应用中使用QThread

3 投票
1 回答
1056 浏览
提问于 2025-04-17 22:39

我写了一个用C++开发的Qt应用程序,这个程序嵌入了Python解释器,并使用PyQt来创建可以编写脚本的用户界面,用于数据分析。Python代码会对数据进行一些计算,然后返回一个QWidget(里面包含各种图表等),这个QWidget会被插入到主应用程序中。

我想从Python中创建一个新的QThread,这样控制权就能返回给C++应用程序,这样就不会因为重的计算而阻塞运行图形界面的主线程。问题是,一旦控制权返回给C++应用程序,线程似乎就会“睡着”,直到再次调用Python解释器,比如在matplotlib图表上发生鼠标悬停事件。我怀疑这可能和Python的全局解释器锁(GIL)有关。

我该如何让从Python创建的QThread在控制权返回到嵌入的应用程序后继续运行呢?

我使用以下代码进行测试。嵌入的应用程序调用test_thread(),并将返回的容器添加到QTabwidget中。这个线程只有在我在matplotlib图表上生成鼠标事件时才会执行。

import time

from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignal, pyqtSlot

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

app = QtGui.QApplication.instance()  

class Worker(QtCore.QObject):
    finished = pyqtSignal()

    def __init__(self):
        QtCore.QObject.__init__(self)

    def do_work(self):
        for i in range(0, 10):
            print 'worker running'
            time.sleep(1)

        self.finished.emit()


class Controller(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)

        self.thread = QtCore.QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.do_work)
        self.thread.finished.connect(app.exit)

    def start_thread(self):
        self.thread.start()


class Container(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)

        self.controller = Controller()
        self.controller.start_thread()

        self.fig = Figure(dpi=72, facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
        self.fig.add_subplot(111)
        self.canvas = FigureCanvas(self.fig)

        self.layout = QtGui.QVBoxLayout()
        self.layout.addWidget(self.canvas)

        self.setLayout(self.layout)


def test_thread():
    container = Container()
    return container

1 个回答

0

CPython是单线程的,这主要是因为有个叫做GIL(全局解释器锁)的东西。简单来说,你不能在Python中同时运行多个线程。

https://wiki.python.org/moin/GlobalInterpreterLock

这里不是Qt在限制你,而是Python在限制你。解决这个问题的办法是为你的“线程”启动一个独立的Python进程,然后使用进程间通信(比如套接字、共享内存等)来让这些进程互相交流。Qt提供了这些进程间通信的接口。你只需要把进程间的通信数据进行序列化。

这段话是基于你在问关于并发的事情。如果你问的是其他问题,我会修改我的回答来澄清。

我如何让从Python创建的QThread在控制权返回给嵌入应用程序后继续运行?

这完全取决于嵌入的应用程序。Python解释器必须被允许继续运行。如果嵌入的应用程序不允许Python继续运行,那么任何试图绕过这个限制的做法都会导致问题。

撰写回答