PyQt5高级界面控件之QThread(十二)

PyQt5高级界面控件之QThread(十二)QThread前言QThread是Qt线程类中最核心的底层类。由于PyQt的跨平台特性,QTHread要隐藏所有与平台相关的代码要使用QThread开始一个线程,可以创建它的一个子类,然后覆盖器其QThread.run()函数classThread(QThread):def__init__(self):super(Thread,self)._…

大家好,又见面了,我是你们的朋友全栈君。

QThread

前言

QThread是Qt的线程类中最核心的底层类。由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码
要使用的QThread开始一个线程,可以创建它的一个子类,然后覆盖其它QThread.run()函数

 class ThreadQThread): def __init __self): superThreadself).__ init __() def runself): #线程相关的代码 pass

接下来创建一个新的线程

thread = Thread()
thread.start()

可以看出,PyQt的线程使用非常简单—-建立一个自定义的类(如thread),自我继承自QThread ,并实现其run()方法即可
在使用线程时可以直接得到Thread实例,调用其start()函数即可启动线程,线程启动之后,会自动调用其实现的run()的函数,该方法就是线程的执行函数
业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了,QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执行一段代码进行资源的初始化和释放操作,更灵活的使用方法是,在自定义的QThread实例中自定义信号,并将信号连接到指定的槽函数,当满足一定的业务条件时发射此信号

QThread类中的常用方法

方法 描述
start() 启动线程
wait() 阻止线程,直到满足如下条件之一
与此QThread对象关联的线程已完成执行(即从run返回时),如果线程完成执行,此函数返回True,如果线程尚未启动,也返回True
等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False
sleep() 强制当前线程睡眠多少秒

QThread类中的常用信号

信号 描述
started 在开始执行run函数之前,从相关线程发射此信号
finished 当程序完成业务逻辑时,从相关线程发射此信号

实例:QThread的使用

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


class MainWidget(QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)
        #设置标题
        self.setWindowTitle('QThread多线程例子')

        #实例化多线程对象
        self.thread = Worker()

        #实例化列表控件与按钮控件
        self.listFile = QListWidget()
        self.btnStart = QPushButton('开始')

        #把控件放置在栅格布局中
        layout = QGridLayout(self)
        layout.addWidget(self.listFile, 0, 0, 1, 2)
        layout.addWidget(self.btnStart, 1, 1)

        #信号与槽函数的连接
        self.btnStart.clicked.connect(self.slotStart)
        self.thread.sinOut.connect(self.slotAdd)

    def slotAdd(self, file_inf):
        #向列表控件中添加条目
        self.listFile.addItem(file_inf)

    def slotStart(self):
        #开始按钮不可点击,线程开始
        self.btnStart.setEnabled(False)
        self.thread.start()


class Worker(QThread):
    sinOut = pyqtSignal(str)

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)
        #设置工作状态与初始num数值
        self.working = True
        self.num = 0

    def __del__(self):
        #线程状态改变与线程终止
        self.working = False
        self.wait()

    def run(self):
        while self.working == True:
            #获取文本
            file_str = 'File index{0}'.format(self.num)
            self.num += 1
            # 发射信号
            self.sinOut.emit(file_str)
            # 线程休眠2秒
            self.sleep(2)


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

运行效果图如下
这里写图片描述

代码分析

在这个例子中,单击开始按钮,会在后台定时读取数据,并把返回的数据显示在界面中,首先使用以下代码进行布局,把列表控件和按钮控件放在栅格布局管理器中

       #实例化列表控件与按钮控件
        self.listFile = QListWidget()
        self.btnStart = QPushButton('开始')

        #把控件放置在栅格布局中
        layout = QGridLayout(self)
        layout.addWidget(self.listFile, 0, 0, 1, 2)
        layout.addWidget(self.btnStart, 1, 1)

然后将按钮的clicked信号连接到槽函数,单击开始触发槽函数

self.btnStart.clicked.connect(self.slotStart)
def slotStart(self):
        #开始按钮不可点击,线程开始
        self.btnStart.setEnabled(False)
        self.thread.start()

比较复杂的是线程的信号,将线程的sinOut信号连接到slotAdd()槽函数,SlotAdd()函数负责在列表控件中动态添加字符串条目

self.thread.sinOut.connect(self.slotAdd)
def slotAddselffile_inf): #向列表控件中添加条目 self.listFile.addItemfile_inf

定义一个线程类,继承自QThread,当线程启动时,执行run()函数

class Worker(QThread):
    sinOut = pyqtSignal(str)

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)
        #设置工作状态与初始num数值
        self.working = True
        self.num = 0

    def __del__(self):
        #线程状态改变与线程终止
        self.working = False
        self.wait()

    def run(self):
        while self.working == True:
            #获取文本
            file_str = 'File index{0}'.format(self.num)
            self.num += 1
            # 发射信号
            self.sinOut.emit(file_str)
            # 线程休眠2秒
            self.sleep(2)

实例二:多线程失败案例

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

global sec
sec=0

def setTime():
    global sec
    sec+=1
    #Led显示数字+1
    lcdNumber.display(sec)

def work():
    #计时器每秒计数
    timer.start(1000)
    for i in range(200000000):
        pass
    timer.stop()
if __name__ == '__main__':
    app=QApplication(sys.argv)
    top=QWidget()
    top.resize(300,120)

    #垂直布局
    layout=QVBoxLayout(top)
    #添加一个显示面板
    lcdNumber=QLCDNumber()
    layout.addWidget(lcdNumber)
    button=QPushButton('测试')
    layout.addWidget(button)

    timer=QTimer()
    #每次计时结束,触发setTime
    timer.timeout.connect(setTime)
    button.clicked.connect(work)

    top.show()
    sys.exit(app.exec_())

失败效果图如下
这里写图片描述

长时间停留在此界面,知道多线程任务完成后,此界面才会动,当耗时程序非常大时,就会造成程序运行失败的假象,实际还是在后台运行的,只是没有显示在主窗口的界面上,当然用户体验也就非常差,那么如何解决这个问题呢,下面实例三进行解答

实例三:分离UI主线程与工作线程

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

global sec
sec = 0


class WorkThread(QThread):
    #实例化一个信号对象
    trigger = pyqtSignal()

    def __int__(self):
        super(WorkThread, self).__init__()

    def run(self):
        #开始进行循环
        for i in range(2000000000):
            pass

        # 循环完毕后发出信号
        self.trigger.emit()


def countTime():
    global sec
    sec += 1
    # LED显示数字+1
    lcdNumber.display(sec)


def work():
    # 计时器每秒计数
    timer.start(1000)
    # 计时开始
    workThread.start()
    # 当获得循环完毕的信号时,停止计数
    workThread.trigger.connect(timeStop)


def timeStop():
    #定时器停止
    timer.stop()
    print("运行结束用时", lcdNumber.value())
    global sec
    sec = 0


if __name__ == "__main__":
    app = QApplication(sys.argv)
    top = QWidget()
    top.resize(300, 120)

    # 垂直布局类QVBoxLayout
    layout = QVBoxLayout(top)

    # 加显示屏,按钮到布局中
    lcdNumber = QLCDNumber()
    layout.addWidget(lcdNumber)
    button = QPushButton("测试")
    layout.addWidget(button)

    #实例化定时器与多线程类
    timer = QTimer()
    workThread = WorkThread()

    button.clicked.connect(work)
    # 每次计时结束,触发 countTime
    timer.timeout.connect(countTime)

    top.show()
    sys.exit(app.exec_())

运行效果,程序主界面的数值会每秒增加1,直到循环结束,这里就避免了主界面长时间不动的尴尬!
这里写图片描述

实例四:事件处理

对于执行很耗时的程序来说,由于PyQt需要等待程序执行完毕才能进行下一步,这个过程表现在界面上就是卡顿,而如果需要执行这个耗时程序时不断的刷新界面。那么就可以使用QApplication.processEvents(),那么就可以一边执行耗时程序,一边刷新界面的功能,给人的感觉就是程序运行很流畅,因此QApplicationEvents()的使用方法就是,在主函数执行耗时操作的地方,加入QApplication.processEvents()

import sys,time
from PyQt5.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout

class WinForm(QWidget):
    def __init__(self,parent=None):
        super(WinForm, self).__init__(parent)
        #设置标题与布局方式
        self.setWindowTitle('实时刷新界面的例子')
        layout=QGridLayout()

        #实例化列表控件与按钮控件
        self.listFile=QListWidget()
        self.btnStart=QPushButton('开始')

        #添加到布局中指定位置
        layout.addWidget(self.listFile,0,0,1,2)
        layout.addWidget(self.btnStart,1,1)

        #按钮的点击信号触发自定义的函数
        self.btnStart.clicked.connect(self.slotAdd)
        self.setLayout(layout)
    def slotAdd(self):
        for n in range(10):
            #获取条目文本
            str_n='File index{0}'.format(n)
            #添加文本到列表控件中
            self.listFile.addItem(str_n)
            #实时刷新界面
            QApplication.processEvents()
            #睡眠一秒
            time.sleep(1)
if __name__ == '__main__':
    app=QApplication(sys.argv)
    win=WinForm()
    win.show()
    sys.exit(app.exec_())

这里写图片描述

相关文件及下载地址

https://download.csdn.net/download/jia666666/10609488

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/140182.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • scrollIntoView 实现「建议收藏」

    scrollIntoView 实现「建议收藏」1.应用场景: 相信大家都曾经遇到过要将屏幕的某一部分滚到到用户视窗里,例如聊天信息的自动滚屏等,这个有不少解决方案: 1.聊天面板的scrolltop=scrollheight2.消息最后加一个element,然后element.scrollIntoView但是如果想要任一容器中间的一个元素,滚到该容器的视窗显示部分…

    2022年6月28日
    29
  • 图片外链方法大全: 免费的图床! 告别新浪图床 和 CDN

    图片外链方法大全: 免费的图床! 告别新浪图床 和 CDN今天给大家公开的是可以图片上传并获取稳定直链的方法,也就是俗称的”图床“;常用的一些免费图床,比如新浪图床可能不好用、图片访问慢;自费购买CDN价格过于昂贵,于是贫穷的我们整理出如下的方法上传图片,用于个人博客、网站等。本文仅列出可公开访问的网页,并只按正常用户操作,手动上传图片获取链接,不公开接口调用方法。防盗链可以通过网站的meta头激活成功教程,在head里插入下方代码:<metaname=”referrer”content=”never”>1、百度

    2022年6月20日
    84
  • Apache和Nginx有什么区别

    Apache和Nginx有什么区别Apache和Nginx最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;而nginx是异步的,多个连接(万级别)可以对应一个进程。区别:Apacheapache的rewrite比nginx强大,在rewrite频繁的情况下,用apacheapache模块多apache更为成熟,少bugapache超稳定apache对PHP支持比较交单,nginx需要配合其他后端用apche在处理动态请求有优势,nginx在这方面是鸡肋,一般动态请求用apache去做,nginx适合静态

    2022年5月7日
    37
  • 下载scrapy失败_手机安装包无法安装怎么办

    下载scrapy失败_手机安装包无法安装怎么办Scrapy安装有问题的,按照这个路径配置下anaconda的环境变量然后进入pycharm里的工作目录输入pipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simplescrapy安装完成后在cmd中输入scrapy,显示下图内容则证明安装成功:…

    2022年9月17日
    7
  • java rmi与dubbo

    java rmi与dubbo首先得知道什么是分布式,以及和集群的区别?分布式:一个业务分拆成多个子业务,部署在不同的服务器上,多半是为了业务解耦,不同的业务可以分别部署,互不干扰,只在需要时相互调用,提升效率。集群:同一个业务,部署在多个服务器上,多半是为了解决高并发,高访问量,提高系统性能。RMIRMI(RemoteMethodInvocation)即远程方法调用,是java在JDK1.1中实现的一…

    2022年6月16日
    35
  • 周鸿祎经典语录

    周鸿祎经典语录 ·如果是你想干的事情,在别人看起来可能是很难的一件事,不过你自己很喜欢,你不会觉得很苦。我开始创业那会是28岁。对我来讲,我创业的目的不是为了自己当老板,我希望有一个平台有一个环境,我可以控制一些资源,让我去创造一个新的产品和服务;  ·我不会顾忌别人怎么看,或者顾忌到放弃什么东西。我喜欢两句诗“天生我才必有用,千金散尽还复来”。当年离开方正,也是这样仰天大笑出门去……  ·如果说…

    2022年7月26日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号