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 相关文章推荐
python实现随机梯度下降(SGD)
Mar 24 Python
利用Python如何生成hash值示例详解
Dec 20 Python
Python Pandas找到缺失值的位置方法
Apr 12 Python
Python3.7中安装openCV库的方法
Jul 11 Python
python快速编写单行注释多行注释的方法
Jul 31 Python
python基于FTP实现文件传输相关功能代码实例
Sep 28 Python
Python笔记之facade模式
Nov 20 Python
TensorFlow基本的常量、变量和运算操作详解
Feb 03 Python
在python中利用dict转json按输入顺序输出内容方式
Feb 27 Python
使用Django清空数据库并重新生成
Apr 03 Python
python实现移动木板小游戏
Oct 09 Python
Python超详细分步解析随机漫步
Mar 17 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实现图片转换成ASCII码的方法
2015/04/03 PHP
php微信公众账号开发之前五个坑(一)
2016/09/18 PHP
初探jquery——表单应用范例
2007/02/20 Javascript
Python脚本后台运行的几种方式
2015/03/09 Javascript
Jsonp post 跨域方案
2015/07/06 Javascript
jQuery取消特定的click事件
2016/02/29 Javascript
JS无缝滚动效果实现方法分析
2016/12/21 Javascript
TypeScript学习之强制类型的转换
2016/12/27 Javascript
详解支持Angular 2的表格控件
2017/01/19 Javascript
关于Bootstrap按钮组件消除黄框的方法
2017/05/19 Javascript
Vue项目中最新用到的一些实用小技巧
2018/11/06 Javascript
详解微信小程序scroll-view横向滚动的实践踩坑及隐藏其滚动条的实现
2019/03/14 Javascript
微信小程序自定义组件components(代码详解)
2019/10/21 Javascript
详解Vue的组件中data选项为什么必须是函数
2020/08/17 Javascript
python登录豆瓣并发帖的方法
2015/07/08 Python
matplotlib设置legend图例代码示例
2017/12/19 Python
Python冲顶大会 快来答题!
2018/01/17 Python
快速解决pyqt5窗体关闭后子线程不同时退出的问题
2019/06/19 Python
Python3使用xml.dom.minidom和xml.etree模块儿解析xml文件封装函数的方法
2019/09/23 Python
CSS Grid布局教程之浏览器开启CSS Grid Layout汇总
2014/12/30 HTML / CSS
详解Html5页面实现下载文件(apk、txt等)的三种方式
2018/10/22 HTML / CSS
澳大利亚UGG工厂直销:Australian Ugg Boots
2017/10/14 全球购物
女装和独特珠宝:Sundance Catalog
2018/09/19 全球购物
Steiff台湾官网:德国金耳釦泰迪熊
2019/12/26 全球购物
咖啡书吧创业计划书
2014/01/13 职场文书
运动会入场词50字
2014/02/20 职场文书
岗位说明书标准范本
2014/07/30 职场文书
加强作风建设心得体会
2014/10/22 职场文书
关于颐和园的导游词
2015/01/30 职场文书
2015年幼儿园中班开学寄语
2015/05/27 职场文书
2016新年年会主持词
2015/07/06 职场文书
2016入党积极分子心得体会
2016/01/06 职场文书
培训心得体会怎么写
2016/01/25 职场文书
高三物理教学反思
2016/02/20 职场文书
《有余数的除法》教学反思
2016/02/22 职场文书
浅谈Java父子类加载顺序
2021/08/04 Java/Android