Python PyQt5——QThread使用方法与代码实践

这篇具有很好参考价值的文章主要介绍了Python PyQt5——QThread使用方法与代码实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

QThread

在 GUI 程序中,如果想要让程序执行一项耗时的工作,例如下载文件、I/O 存取等,深度学习模型推理,直接在 UI 线程中进行这些操作会导致整个界面卡住,出现无响应的状态。为了避免这种情况,可以将这些耗时任务放在另一个线程中执行。在 PyQt 中,可以使用 QThread 来实现这一点。

基本用法

在 PyQt5 中,要使用 QThread 创建一个线程,需要创建 QThread 的子类,并重写 QThread.run() 函数。以下是一个示例,WorkerThread 类继承自 QThread 并重写了 run() 方法:

class WorkerThread(QThread):
    def __init__(self):
        super().__init__()
    def run(self):
        # 执行耗时任务

接下来,可以使用 QThread.start() 来启动线程。在构造 WorkerThread 后,不会立即执行 run() 方法,直到调用 QThread.start() 才开始执行。如果主线程需要等待线程执行完毕,可以使用 QThread.wait(),它会等待线程完成才返回。但请注意,如果线程中包含无限循环,QThread.wait() 将会无限期等待。

下面是一个简单的示例:

import sys
import time
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QThread

class WorkerThread(QThread):
    def __init__(self):
        super().__init__()
    def run(self):
        for i in range(3):
            time.sleep(1)
            print('WorkerThread::run ' + str(i))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    print('main')
    work1 = WorkerThread()
    work2 = WorkerThread()
    work1.start()
    work2.start()
    work1.wait()
    work2.wait()
    print('end of main')
    sys.exit(app.exec_())

在这个 run 函数中,有一个循环执行 3 次,并在每次循环后休眠 1 秒,然后输出一条消息。从输出结果可以看出,主线程会等待两个 WorkerThread 线程都完成后才输出 “end of main” 消息,这证明了使用 QThread.wait() 是有效的。

错误堵塞机制

在 PyQt5 开发过程中,新手容易错误地使用线程,导致界面卡住或变黑。例如,一个下载文件的程序,按下按钮后会执行大约 10 秒的工作。如果在 UI 线程中直接执行这些操作,会发现整个 GUI 程序无法进行其他操作,界面会卡住或变黑。

正确的做法是在 WorkerThread 中定义两个信号,分别为 trigger 和 finished。finished 信号在工作完成后发送,而 trigger 信号在执行过程中发送。使用 pyqtSignal 来定义自定义信号,并指定发送到目标函数的参数类型。例如,trigger = pyqtSignal(str)。

整个程序在按下按钮后,会开启另一个线程,每秒更新一次秒数到标签上,通过 self.trigger.emit(str(i+1)) 发射信号并传递第几秒的参数。第 5 秒时结束线程,并通过 self.finished.emit() 发射结束信号。

下面是一个示例代码:

import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import QThread, pyqtSignal

class WorkerThread(QThread):
    trigger = pyqtSignal(str)
    finished = pyqtSignal()

    def __init__(self):
        super().__init__()
    def run(self):
        for i in range(5):
            time.sleep(1)
            self.trigger.emit(str(i+1))
            print('WorkerThread::run ' + str(i))
        self.finished.emit()

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('my window')
        self.setGeometry(50, 50, 200, 150)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.mylabel = QLabel('press button to start thread', self)
        layout.addWidget(self.mylabel)
        self.mybutton = QPushButton('start', self)
        self.mybutton.clicked.connect(self.startThread)
        layout.addWidget(self.mybutton)
        self.work = WorkerThread()

    def startThread(self):
        self.mybutton.setDisabled(True)
        self.work.start()
        self.work.trigger.connect(self.updateLabel)
        self.work.finished.connect(self.threadFinished)
        self.updateLabel(str(0))

    def threadFinished(self):
        self.mybutton.setDisabled(False)

    def updateLabel(self, text):
        self.mylabel.setText(text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())

结果如下:

main
press button to start thread
1
2
3
4
end of main

如果不想在 threadFinished 函数中只是执行一段代码,可以省略 threadFinished 函数,改用 lambda 表达式。例如,将 self.mybutton.setDisabled(False) 操作写在 self.work.finished.connect() 中的 lambda 表达式里。

在 QWidget 里使用 QThread

在 PyQt 程序中,主线程就是所说的 UI 线程,UI 线程会处理所有控件的事务。因此,如果有耗时的工作需要执行,通常不会将其放在 UI 线程中,因为这样做会阻止其他控件的更新,导致界面卡顿或程序无响应。解决这个问题的方法是创建另一个线程来处理这些耗时的工作。

在 WorkerThread 中新增了两个信号,分别命名为 trigger 和 finished。finished 信号在工作完成后发送,而 trigger 信号在执行过程中发送。当需要自定义信号时,使用 pyqtSignal 来定义要发送到目标函数的函数原型,例如在下面的示例中,trigger = pyqtSignal(str) 表示 trigger 信号会携带一个字符串参数。

整个程序的工作流程是:当按下按钮后,会启动另一个线程,这个线程每一秒更新一次秒数到标签上。通过 self.trigger.emit(str(i+1)) 发送 trigger 信号,并传递当前的秒数作为参数。当到达第 5 秒时,线程结束,并通过 self.finished.emit() 发送 finished 信号。

这种设计允许我们在不影响 UI 响应性的情况下执行长时间的任务,同时还能更新 UI 显示当前的进度或状态。通过使用信号和槽机制,可以在工作线程和 UI 线程之间安全地传递信息,确保程序的流畅运行。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import time
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout,
                             QLabel, QPushButton)
from PyQt5.QtCore import QThread, pyqtSignal


class WorkerThread(QThread):
    trigger = pyqtSignal(str)
    finished = pyqtSignal()

    def __init__(self):
        super().__init__()

    def run(self):
        for i in range(5):
            time.sleep(1)
            self.trigger.emit(str(i+1))
            print('WorkerThread::run ' + str(i))
        self.finished.emit()


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('my window')
        self.setGeometry(50, 50, 200, 150)

        layout = QVBoxLayout()
        self.setLayout(layout)

        self.mylabel = QLabel('press button to start thread', self)
        layout.addWidget(self.mylabel)

        self.mybutton = QPushButton('start', self)
        self.mybutton.clicked.connect(self.startThread)
        layout.addWidget(self.mybutton)

        self.work = WorkerThread()

    def startThread(self):
        self.mybutton.setDisabled(True)
        self.work.start()
        self.work.trigger.connect(self.updateLabel)
        self.work.finished.connect(self.threadFinished)
        self.updateLabel(str(0))

    def threadFinished(self):
        self.mybutton.setDisabled(False)

    def updateLabel(self, text):
        self.mylabel.setText(text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())

如果你不希望 threadFinished 函数仅仅是执行一行代码,而是想去掉 threadFinished 函数,可以将 self.mybutton.setDisabled(False) 操作写在 self.work.finished.connect() 里的 lambda 表达式中。以下是使用 lambda 表达式的示例:

def startThread(self):
    self.mybutton.setDisabled(True)
    self.work.start()
    self.work.trigger.connect(self.updateLabel)
    # self.work.finished.connect(self.threadFinished)
    self.work.finished.connect(lambda: self.mybutton.setDisabled(False))
    self.updateLabel(str(0))

# def threadFinished(self):
#    self.mybutton.setDisabled(False)

def updateLabel(self, text):
    self.mylabel.setText(text)

这样,当 finished 信号被触发时,lambda 表达式中的代码将会执行,即启用按钮。文章来源地址https://www.toymoban.com/news/detail-860199.html

到了这里,关于Python PyQt5——QThread使用方法与代码实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用python和pyqt5轻松上手人脸识别系统(含代码)

    最近在做一个人脸识别考勤系统,已经总结和记录了大部分内容,算是比较完善啦!后续把剩下的搞完,感兴趣的同学可以关注一下哦~ 文末给出了代码获取方式,请自行获取食用~ B站:马上就更!!!_bilibili CSDN:使用python和pyqt5轻松上手人脸识别系统(含代码)_百年后封笔

    2024年02月11日
    浏览(85)
  • Qt QThread的moveToThread方法使用

    从 Qt4.4 版本之后,因为 QThread 的 run 方法创建新线程这样实现与 Qt 设计的理念不符,Qt 主推使用 moveToThread 方法来创建新线程。 QThread应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将

    2024年02月14日
    浏览(34)
  • Python——Pyqt5的数据可视化小工具(完整代码)

    作业要求:【都已经打包放网上了,有缘人需要就自取】 一份报告书 (在全球变暖背景下碳中和对各国的二氧化碳排放量的影响项目报告书) 一份代码 作业包: python数据可视化小工具.zip - 蓝奏云 大一的时候,当时的python作业,交完作业后,忘记记录了(难受.jpg) 现在大

    2024年02月02日
    浏览(47)
  • Python 使用 PyQt5 实现截图

    原文链接:https://www.cpweb.top/2566 使用 PyQt5 实现截图功能,功能点: • 选框截图:鼠标左键选择区域双击截屏,右击重新截图。 • 全屏截图:不选区域,直接鼠标双击截全屏。

    2024年02月14日
    浏览(38)
  • Pycharm中成功配置PyQt5(External Tools),设计好界面直接生成python代码

    在Pycharm中设置好Python环境,点击File-Settings-Project-Python Interpreter  设置好后退出,点击窗口下的Terminal,输入  同样的方法安装PyQt5-tools: 点击File-Settings-Tools-External Tools,点击+号    需要配置三个tools ,分别是 QtDesigner、PyUICS、Pyrcc,其中QtDesigner是qt 设计师,PyUics是把UI界面转

    2024年02月11日
    浏览(60)
  • Python使用PyQt5实现计算平方或者立方

    此源码为直播时讲解的源码,我加了注释

    2024年02月11日
    浏览(35)
  • Python使用PyQt5实现指定窗口置顶

    工作中,同事随口提了一句:要是能让WPS窗口置顶就好了,老是将窗口切换来切换去的太麻烦了。 然后,这个奇怪的点子引起了本人的注意,那就试试能不能实现吧。 不知道是不是我手法或版本的缘故,用了网上找的代码都是窗口弹出而已,并没有把它置顶,可以参考以下

    2024年02月13日
    浏览(52)
  • PyQt6 QMessageBox的使用方法

    放置一个QPushButton就可以。 点击按钮显示QMessageBox

    2024年02月11日
    浏览(37)
  • Pyqt Model View 的使用方法

    全是嵌套的callback 的实现方法,层级分离,通过类的继承,拓展基础类的功能,通用性不错,省了自己在,调用原始方法,构建view 和 控制数据库,再将数据同步到板卡,利用现成的基础类,自己在拓展一个功能就🆗了 C 没有继承和多态,一言难尽,Python 的继承和多态应该是最容易理解的 添

    2024年02月07日
    浏览(57)
  • 使用VS Code Python和PYQT5实现简单的按键精灵

    创建工程目录,创建UI文件,创建Main Window 使用思维导图进行简单的需求整理 由于没怎么用过pyqt,简单排版一下,后续也可以再用Vertical Layout和Horizontal Layout进行排版优化,按设计,三个文本框进行-功能说明,-时间间隔填写,-状态说明的显示,一个按钮可以选择-删除文件,

    2024年01月21日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包