当前位置: 首页 > 知识库问答 >
问题:

PYQT5中的QThreads:这是官方QThread文档的C++到Python的正确翻译吗?

丰博
2023-03-14

关于如何实例化和使用qthread的官方文档可以在以下位置找到:http://doc.qt.io/qt-5/qthread.html

文档描述了两种基本方法:(1)worker-object方法和(2)QThread子类方法。
我在几篇文章中读到第二种方法不好,所以让我们集中讨论第一种方法。

编辑:
@ekhumoro给我指出了下面这篇有趣的文章:https://woboq.com/blog/qthread-you-were-noth-down-so-wurn.html。显然,这两种方法(1)和(2)各有其优点:

由于我确实需要在QApplication线程和新的QThread之间进行某种通信(我相信signal-slot是一种很好的通信方式),所以我将使用Worker-Object方法

我已经复制粘贴了Worker-Object方法的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 &);
};

 

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 appapp.exit()添加到handleresults(..)插槽中。谢谢@matic指出这一点!

注意(4):
我(通过一些文档)发现了这种结束应用程序的方法。第一个codeline结束WorkerThread(通过终止其事件循环)。第二个codeline结束mainthread(也通过终止其事件循环)。

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

 

将代码全局应用程序app.exit()添加到handleResults(..)插槽可以修复挂起问题。但是背景到底发生了什么呢?这些代码是在扼杀工人线程吗?还是主QApplication线程?

有没有一种方法可以在不杀死主QApplication线程的情况下杀死辅助线程?

1.仍然不确定…

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

我确实像预期的那样把行打印出来了。我还尝试以类似的方式检查主线程的杀死:

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

不幸的是,这行不能打印出来。为什么?

特此提供我当前的系统设置:
 、 、 、 > Qt5(QT_version_str=5.10.1)
    > PyQt5(pyqt_version_str=5.10.1)
    > Python 3.6.3
 · · · > Windows 10,64位

共有1个答案

葛霄
2023-03-14

您的Python示例应用程序需要以某种方式退出,否则它只是在控制器对象初始化后就坐在那里。

最简单的方法是将示例中的handleResults函数更改为:

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

希望能有所帮助。

 类似资料:
  • etcd 是一个分布式键值对存储,设计用来可靠而快速的保存关键数据并提供访问。通过分布式锁,leader选举和写屏障(write barriers)来开启可靠的分布式协同。etcd集群是为高可用,持久性数据存储和检索而准备。 开始 现在etcd的用户和开发者可以从 下载并构建 etcd开始。在获取etcd之后,跟随 quick demo 来看构建和操作etcd集群的基本内容。 使用etcd开发 开

  • 内容 Why etcd (TODO) 理解数据模型 理解API 术语 API保证 Internals (TODO) 额外说明 这些内容主要是介绍 etcd 的概念和实现细节,适合希望深入了解 etcd 的同学。 内容来自 github 官网,地址: https://github.com/coreos/etcd/tree/master/Documentation/learning

  • 内容 开发指南的内容如下: 搭建本地集群 和 etcd 交互 gRPC etcd core 和 etcd concurrency API 参考 经过gRPC 网关的 HTTP JSON API gRPC命名与发现 客户端 和 代理 命名空间 内嵌的 etcd 试验性的 API 和特性 系统限制 注: 内容来自 https://github.com/coreos/etcd/tree/master/D

  • 内容 搭建etcd集群 搭建etcd网关 在容器内运行etcd集群 配置 加密(TODO) Monitoring 维护 理解失败 灾难恢复 性能 版本 支持平台 额外说明 这些内容相当于是 etcd3 的管理手册,针对的是 etcd3 的管理者(如运维人员)。对于运维人员,需要重点阅读这些内容。 内容来自 github 官网,地址: https://github.com/coreos/etcd/t

  • 问题内容: 我在Django 1.6上无法正常使用翻译存在问题。我已将此添加到我的settings.py中: 还添加了中间件: 以及每当我使用应为l10nd的字符串时的* .py文件: 我的模板开始于: 在模板内部,我使用了trans占位符。例如 我在locale / de / LC_MESSAGES / django.po中提供了翻译: 问题答案: 添加到并将其设置如下: 请注意,它必须是一个元

  • Selenium Python bindings 提供了一个简单的 API,让你使用 Selenium WebDriver 来编写功能/校验测试。 通过 Selenium Python 的 API,你可以非常直观的使用 Selenium WebDriver 的所有功能。