如何在 PyQt 中使用 QThreads 双向设置信号和槽?

2024-05-01

这是基于 ekhumoro 答案的后续问题here https://stackoverflow.com/a/52005755/4464653 and here https://stackoverflow.com/a/20818401/4464653.


我想明白,当一个槽被正确定义时pyqtSlot并分配给QThread(例如与moveToThread()),它将在此 QThread 中执行,而不是在调用者中执行。此外,建立与Qt.QueuedConnection or Qt.AutoConnection也是必需的。

I wrote Code to test this. My goal is to achive something quite simple like this: enter image description here
Gui with a button, that starts some time-consuming-work and return with a result to display back in the GUI.

from PyQt5.Qt import *

class MainWindow(QMainWindow):
    change_text = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)

        print('main running in:', QThread.currentThread())
        thread = Thread(change_text, self)
        thread.start()
        self.button.clicked.connect( thread.do_something_slow, Qt.QueuedConnection)
        self.change_text.connect(self.display_changes, Qt.QueuedConnection)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)

class Thread(QThread):
    def __init__(self, signal_to_emit, parent):
        super().__init__(parent)
        self.signal_to_emit = signal_to_emit
        #self.moveToThread(self) #doesn't help

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.signal_to_emit.emit('I did something')

if __name__ == '__main__':
    app = QApplication([])
    main = MainWindow()
    main.show()
    app.exec()

但是.. gui 被阻塞并且槽在主线程中被调用。
我缺少什么?必须是小东西(我希望)。


问题是你很困惑QThread is a Qt线程,即Qt创建的新线程,但是没有,QThread是一个类handles本机线程,仅run()方法正在另一个线程上运行,其他方法位于该线程所在的线程中QThread生活,这是一个QObject,

QObject 存在于哪个线程中?

线程中的一个QObject生命是父线程的生命,如果它没有父线程,它将是创建它的线程。另一方面,一个QObject可以使用移动到另一个线程moveToThread(),并且它的所有子项也会移动。仅有的moveToThread()可以使用,如果QObject没有父母,否则会失败。


使用方法QThread是创建一个继承自的类QThread并重写 run 方法并调用start()以便它开始运行run(), 在里面run()方法将完成繁重的任务,但在您的情况下不能使用这种形式,因为任务不会连续执行。比繁重的任务更好的选择是QObject, 然后QObject将其移动到另一个线程。

class MainWindow(QMainWindow):
    change_text = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)
        print('main running in:', QThread.currentThread())
        # A Worker without a parent is created 
        # so that it can be moved to another thread.
        self.worker = Worker(self.change_text)
        thread = QThread(self) 
        self.worker.moveToThread(thread)
        thread.start()
        # All methods of self.worker are now executed in another thread.
        self.button.clicked.connect(self.worker.do_something_slow)
        self.change_text.connect(self.display_changes)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)


class Worker(QObject):
    def __init__(self, signal_to_emit, parent=None):
        super().__init__(parent)
        self.signal_to_emit = signal_to_emit

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.signal_to_emit.emit('I did something')

另一方面,没有必要指示连接类型,因为默认情况下它是Qt::AutoConnection,这种类型的连接在运行时决定是否使用Qt::DirectConnection如果接收器与发射信号位于同一根电线中,否则,Qt::QueuedConnection用来。正如您所意识到的,Worker 没有父级,因此要使其具有与类相同的生命周期,它必须是它的一个属性,否则它将是一个被消除的局部变量。另一方面,请注意QThread接收父级而不是 MainWindow,所以QThread将驻留在 GUI 线程中,但它处理辅助线程。


有了 Worker 概念,change_text 信号最好不再属于 GUI,而是属于能够更好地解耦对象的 Worker。

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)

        print('main running in:', QThread.currentThread())
        self.worker = Worker()
        thread = QThread(self)
        self.worker.moveToThread(thread)
        thread.start()
        self.button.clicked.connect(self.worker.do_something_slow)
        self.worker.change_text.connect(self.display_changes)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)

class Worker(QObject):
    change_text = pyqtSignal(str)

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.change_text.emit('I did something')
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 PyQt 中使用 QThreads 双向设置信号和槽? 的相关文章

  • 如何在 Python 中使这个随机文本生成器更加高效?

    我正在研究一个随机文本生成器 不使用马尔可夫链 目前它的工作没有太多问题 首先 这是我的代码流程 输入一个句子作为输入 这称为触发字符串 被分配给一个变量 获取触发字符串中最长的单词 在所有古腾堡计划数据库中搜索包含该单词的句子 无论大写还
  • 使用 pycharm 进行交互式 shell 调试

    我是 PyCharm 新手 我已经使用 IDLE 很长时间了 在IDLE中执行脚本后使用Python对象非常方便 有没有办法在使用 PyCharm 与交互式 python shell 执行后使用脚本对象 例如 我们有一个 测试 项目 其中包
  • 使用组合时如何解决循环依赖?

    我遇到了如下所示的情况 其中每个类都需要另一个类 并且它创建了循环依赖关系 我在使用 ctypes 包装一些 C 代码时遇到了这种情况 已经有很多关于这个主题的帖子 但我发现它们没有帮助 我需要一些例子 Module A from B im
  • Windows 7 64位 libsvm 和 python 错误:找不到函数“svm_get_sv_indices”

    我正在使用 Windows 7 64 位 我已经安装了 Python 2 7 3 32 位版本 和 libsvm 3 13 当我尝试启动导入 svmutil 的简单 py 文件时 出现错误 C libsvm 3 13 python gt p
  • 为不带引号的函数获取字符串参数

    我有一个函数 用于从 URL 下载文件并将其写入磁盘 并施加特定的文件扩展名 目前 它看起来像这样 import requests import os def getpml url filename psc requests get url
  • LSTM - 一段时间后预测相同的常数值

    我有一个变量 我想预测未来 30 年的情况 不幸的是我没有很多样品 df pd DataFrame FISCAL YEAR 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 199
  • 使用 python 将 bibtex 文件转换为 html (也许是 pybtex?)

    您好 我想解析 bibtex 出版物文件并对特定字段 例如年份 进行排序并过滤某些内容 然后将其放在网站上 我遇到了 pybtex 它可以读取和解析 bibtex 文件 但它基本上没有记录 我不知道如何对条目进行排序 pybtex 是可行的
  • Redis 队列工作程序在 utcparse 中崩溃

    我正在尝试按照以下教程获得基本的 rq 工作 https blog miguelgrinberg com post the flask mega tutorial part xxii background jobs https blog m
  • 调整pandas read_sql_query NULL值处理?

    当我做 from sqlalchemy import create engine import pandas as pd engine create engine sqlite conn engine connect conn execut
  • 根据另一个非索引数组中的值从 numpy 数组中选择元素

    假设我有以下两个数组 a array 1 L 74 423088306605 5 H 128 05441039929008 2 L 68 0581377353869 0 H 88 15726964130869 4 L 97 45015825
  • 是否可以使用 csv.DictReader 保持列顺序?

    例如 我的 csv 有如下列 ID ID2 Date Job No Code 我需要以相同的顺序写回各列 这dict立即打乱了顺序 所以我相信这更多是读者的问题 蟒蛇的dicts 在 3 6 之前不维持顺序 但是 无论如何 在该版本中csv
  • Python:像石英一样的事件调度程序[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • 使用 python 更改目录

    我碰巧发现我无法从 python 代码中更改实际目录 我的测试程序如下 from os import system def sh script system bash c s script sh cd home sh pwd 的输出pwd
  • 在 Jupyter 笔记本中使用 PySpark 读取 XML

    我正在尝试读取 XML 文件 df spark read format com databricks spark xml load path to my xml 并收到以下错误 java lang ClassNotFoundExceptio
  • Python Sqlite3 获取 Sqlite 连接路径

    给定一个 sqlite3 连接对象 如何检索 sqlite3 文件的文件路径 The Python 连接对象 http github com python cpython blob master Modules sqlite connect
  • 如何使用Python3.4在tornado中进行异步mysql操作?

    我现在使用Python3 4 我想在Tornado中使用异步mysql客户端 我已经发现torndb https github com bdarnell torndb但在阅读其源代码后 我认为它无法进行异步mysql操作 因为它只是封装了M
  • Django中的自动递增值

    我在 django 中有一个表并尝试自动递增它的序列号 在自定义模板中 for 循环用于变量 自定义模板 for i in getodeskview tr td 1 td td i odesk id td td i hours td td
  • Django 多个外键,相同的相关名称

    我想创建一个模型 1 其中具有相同其他模型 2 的多个外键 我希望这些外键具有相同的related name因为每个外键将指向 model 2 的不同实例 因为我需要所有外键的一个反向关系 也许一个例子会更明确 class Parent M
  • 加载腌制字典对象或加载 JSON 文件哪个更快? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 什么更快 A Unpickling 加载 一个 pickled 字典对象 使用pickle load or B 使用以下命令将 JSON

随机推荐