python GUI库图形界面开发之PyQt5线程类QThread详细使用方法


Posted in Python onFebruary 26, 2020

QThread是Qt的线程类中最核心的底层类。由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码

要使用的QThread开始一个线程,可以创建它的一个子类,然后覆盖其它QThread.run()函数

class Thread(QThread):
  def __init __(self):
    super(Thread,self).__ init __()
  def run(self):
    #线程相关的代码
    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_())

运行效果图如下

python GUI库图形界面开发之PyQt5线程类QThread详细使用方法

代码分析

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

#实例化列表控件与按钮控件
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 slotAdd(self,file_inf):
    #向列表控件中添加条目
    self.listFile.addItem(file_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_())

失败效果图如下

python GUI库图形界面开发之PyQt5线程类QThread详细使用方法

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

分离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,直到循环结束,这里就避免了主界面长时间不动的尴尬!

python GUI库图形界面开发之PyQt5线程类QThread详细使用方法

QThread线程事件处理实例

对于执行很耗时的程序来说,由于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_())

python GUI库图形界面开发之PyQt5线程类QThread详细使用方法

本文详细介绍了python GUI库PyQt5的线程类QThread详细使用方法,想了解更多相关知道请查看下面的相关链接

Python 相关文章推荐
Python3网络爬虫之使用User Agent和代理IP隐藏身份
Nov 23 Python
windows下Virtualenvwrapper安装教程
Dec 13 Python
python机器学习实战之K均值聚类
Dec 20 Python
Python实现输出某区间范围内全部素数的方法
May 02 Python
python dataframe常见操作方法:实现取行、列、切片、统计特征值
Jun 09 Python
Python中.join()和os.path.join()两个函数的用法详解
Jun 11 Python
利用Python如何批量更新服务器文件
Jul 29 Python
python如何基于redis实现ip代理池
Jan 17 Python
Python虚拟环境的创建和包下载过程分析
Jun 19 Python
Python通过递归函数输出嵌套列表元素
Oct 15 Python
Python 制作查询商品历史价格的小工具
Oct 20 Python
弄清Pytorch显存的分配机制
Dec 10 Python
Python处理PDF与CDF实例
Feb 26 #Python
用Python绘制漫步图实例讲解
Feb 26 #Python
Django单元测试中Fixtures的使用方法
Feb 26 #Python
python 解压、复制、删除 文件的实例代码
Feb 26 #Python
Python递归调用实现数字累加的代码
Feb 25 #Python
python烟花效果的代码实例
Feb 25 #Python
python GUI库图形界面开发之PyQt5控件QTableWidget详细使用方法与属性
Feb 25 #Python
You might like
PHP+SQL 注入攻击的技术实现以及预防办法
2011/01/27 PHP
PHP中strtotime函数使用方法分享
2012/01/10 PHP
PHP json格式和js json格式 js跨域调用实现代码
2012/09/08 PHP
CodeIgniter使用phpcms模板引擎
2013/11/12 PHP
老生常谈PHP 文件写入和读取(必看篇)
2017/05/22 PHP
javascript转换字符串为dom对象(字符串动态创建dom)
2010/05/10 Javascript
nodejs实用示例 缩址还原
2010/12/28 NodeJs
jquery实现excel导出的方法
2013/04/04 Javascript
Enter转换为Tab的小例子(兼容IE,Firefox)
2013/11/14 Javascript
javascript中if和switch,==和===详解
2015/07/30 Javascript
javascript闭包概念简单解析(推荐)
2016/06/03 Javascript
JavaScript中setter和getter方法介绍
2016/07/11 Javascript
jQuery简单实现列表隐藏和显示效果示例
2016/09/12 Javascript
jQuery通过ajax快速批量提交表单数据
2016/10/25 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
React降级配置及Ant Design配置详解
2018/12/27 Javascript
react同构实践之实现自己的同构模板
2019/03/13 Javascript
解决vant title-active-color与title-inactive-color不生效问题
2020/11/03 Javascript
js 数据类型判断的方法
2020/12/03 Javascript
利用Python的Django框架中的ORM建立查询API
2015/04/20 Python
Python按行读取文件的实现方法【小文件和大文件读取】
2016/09/19 Python
Python基于Floyd算法求解最短路径距离问题实例详解
2018/05/16 Python
python psutil模块使用方法解析
2019/08/01 Python
python中@property和property函数常见使用方法示例
2019/10/21 Python
Python工程师必考的6个经典面试题
2020/06/28 Python
Python如何操作docker redis过程解析
2020/08/10 Python
python tkinter的消息框模块(messagebox,simpledialog)
2020/11/07 Python
css3隔行变换色实现示例
2014/02/19 HTML / CSS
全面解析HTML5中的标准属性与自定义属性
2016/02/18 HTML / CSS
Whistles官网:英国女装品牌
2020/08/14 全球购物
党员个人自我评价
2015/03/03 职场文书
2015年社会实践个人总结
2015/03/06 职场文书
慈善募捐倡议书
2015/04/27 职场文书
解决hive中导入text文件遇到的坑
2021/04/07 Python
教你利用Nginx 服务搭建子域环境提升二维地图加载性能的步骤
2021/09/25 Servers
golang为什么要统一错误处理
2022/04/03 Golang