代码之家  ›  专栏  ›  技术社区  ›  K.Mulier

PQQT5中的Q线程:这是正确的C++到Python的官方QToC文档翻译吗?

  •  1
  • K.Mulier  · 技术社区  · 8 年前

    关于如何实例化和使用 QThread 可以在这里找到: http://doc.qt.io/qt-5/qthread.html

    文档描述了两种基本方法:(1)工作对象方法和(2) Q螺纹 子类方法。
    我在几篇文章中读到了第二种方法不好,所以让我们集中讨论第一种方法。


    编辑:
    @Ekhomigo向我指出了以下有趣的文章: https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html . 显然,两种方法(1)和(2)都有各自的优点:

    根据经验:

  • 如果您在线程中不真正需要一个事件循环,那么您应该子类化。
  • 如果您需要一个事件循环并处理线程中的信号和槽,那么您可能不需要子类。
  • 因为我确实需要在Qapplication线程和新的qthread之间进行某种通信(我相信信号槽是一种很好的通信方式),所以我将使用 工作对象方法 .


    1。C++中的WorkObjor方法

    我已经复制粘贴了C++代码了 工作对象方法 (摘自官方qt5文件,见 网址:http://doc.qt.io/qt-5/qthread.html ):

    class Worker : public QObject
    {
        Q_OBJECT
    
    public slots:
        void doWork(const QString &parameter) {
            QString result;
            /* ... here is the expensive or blocking operation ... */
            emit resultReady(result);
        }
    
    signals:
        void resultReady(const QString &result);
    };
    
    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    public:
        Controller() {
            Worker *worker = new Worker;
            worker->moveToThread(&workerThread);
            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
            connect(this, &Controller::operate, worker, &Worker::doWork);
            connect(worker, &Worker::resultReady, this, &Controller::handleResults);
            workerThread.start();
        }
        ~Controller() {
            workerThread.quit();
            workerThread.wait();
        }
    public slots:
        void handleResults(const QString &);
    signals:
        void operate(const QString &);
    };
    


    2。python中的worker对象方法

    我努力把给定的C++代码翻译成Python。如果安装了python 3.6和pyqt5,只需复制粘贴代码并在计算机上运行它。它应该能工作。

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class Worker(QObject):
    
        resultReady = pyqtSignal(str)
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
        @pyqtSlot(str)
        def doWork(self, param):
            result = "hello world"
            print("foo bar")
            # ...here is the expensive or blocking operation... #
            self.resultReady.emit(result)
    
    
    class Controller(QObject):
    
        operate = pyqtSignal(str)
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            # 1. Create 'workerThread' and 'worker' objects
            # ----------------------------------------------
            self.workerThread = QThread()
            self.worker = Worker()          # <- SEE NOTE(1)
            self.worker.moveToThread(self.workerThread)
    
            # 2. Connect all relevant signals
            # --------------------------------
            self.workerThread.finished.connect(self.worker.deleteLater)
            self.workerThread.finished.connect(lambda: print("workerThread finished."))  # <- SEE NOTE(2)
            self.operate.connect(self.worker.doWork)
            self.worker.resultReady.connect(self.handleResults)
    
            # 3. Start the thread
            # --------------------
            self.workerThread.start()
    
        def __del__(self):
            self.workerThread.quit()
            self.workerThread.wait()
    
        @pyqtSlot(str)
        def handleResults(self, param):
            print(param)
            # One way to end application
            # ---------------------------
            # global app      # <- SEE
            # app.exit()      #     NOTE(3)
    
            # Another way to end application
            # -------------------------------
            self.workerThread.quit()   # <- SEE NOTE(4)
            self.thread().quit()
    
    
    if __name__ == '__main__':
        app = QCoreApplication([])
        controller = Controller()
        controller.operate.emit("foo")      # <- SEE NOTE(5)
        sys.exit(app.exec_())
    

    注(1):
    最初我执行了 worker 变量作为构造函数中的局部变量。我把C++样本逐字翻译成Python,这个变量也是C++样本中的一个局部变量。
    正如您在@pschill的注释中看到的,这个局部变量被垃圾收集,因此我无法让线程运行。在做了更改之后,我得到了预期的输出。

    注(2):
    我添加了这一行以准确知道 workerThread 完成。

    注(3):
    显然我需要添加这两个代码行 global app app.exit() handleResults(..) 狭槽。感谢@matic指出这一点!

    注(4):
    我发现(通过一些文档)这种方法可以结束应用程序。第一行代码结束 工作线程 (通过终止事件循环)。第二条代码行结束 mainThread (也通过终止事件循环)。

    注(5):
    在Windows控制台中运行代码时,没有发生任何事情(它只是挂起)。根据@pschill的建议(见下面他的评论),我添加了这个代码行以确保 doWork() 函数被调用。


    三。我的问题

    1. 首先,我想知道我是否从C++到Python的翻译是正确的。请告诉我哪里出错了(如果你发现的话)。

    2. 添加代码行 全球应用程序 应用程序退出() 把手结果(..) 插槽解决了悬挂问题。但是在背景上到底发生了什么呢?这些代码行会杀死工作线程吗?还是主Qapplication线程?

    3. 有没有一种方法可以在不杀死主QApplication线程的情况下杀死工作线程?


    4。一些答案

    1。仍然不确定……


    2。我相信 应用程序退出() 杀死主线程,然后杀死工作线程,因为它属于 类型。我发现工作线程是 迪蒙 键入,因为我插入了代码行 print(threading.current_thread()) doWork(..) 功能。它是印刷的 <_DummyThread(Dummy-1, started daemon 9812)> 。当程序退出时,任何守护进程线程都会自动终止。


    三。是的,我找到了办法!这个 QThread::quit() 功能是你的朋友。官方文件说:

    无效 qthread::退出()
    告诉线程的事件循环退出,返回代码为0(成功)。相当于呼叫 QThread::exit(0) .
    如果线程没有事件循环,则此函数不执行任何操作。
    [ http://doc.qt.io/qt-5/qthread.html#quit]

    所以我的功能 把手结果(..) 现在看起来像这样:

        @pyqtSlot(str)
        def handleResults(self, param):
            print(param)
            self.workerThread.quit()  # Kill the worker thread
            self.thread().quit()      # Kill the main thread
    

    我已经通过在 Controller(..) :

        self.workerThread.finished.connect(lambda: print("workerThread finished."))
    

    我确实按预期把这行打印出来了。我也尝试过用类似的方法检查主线程的终止:

        self.thread().finished.connect(lambda: print("mainThread finished."))
    

    很遗憾,这行没有打印出来。为什么?


    在此,我提供我当前的系统设置:
    >第5季度( QT_VERSION_STR =5.10.1)
    >Pyqt5( PYQT_VERSION_STR =5.10.1)

    >Windows 10,64位

    1 回复  |  直到 8 年前
        1
  •  1
  •   Matic Kukovec    8 年前

    您的python示例应用程序需要以某种方式退出,否则它只位于 Controller 对象已初始化。

    最简单的是改变 handleResults 示例中的函数用于:

    @pyqtSlot(str)
    def handleResults(self, param):
        print(param)
        global app
        app.exit()
    

    希望有帮助。