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

 更新时间:2020年02月26日 08:55:46   作者:jia666666  
这篇文章主要介绍了python GUI库图形界面开发之PyQt5线程QThread类详细使用方法,需要的朋友可以参考下

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_())

运行效果图如下

代码分析

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

#实例化列表控件与按钮控件
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_())

失败效果图如下

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

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

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利用wxPython模块打造ChatGPT式打字效果程序

    Python利用wxPython模块打造ChatGPT式打字效果程序

    这篇文章主要为大家介绍了如何利用Python和wxPython模块打造一个ChatGPT式打字效果程序,从而增强用户体验或提高应用程序的可读性,感兴趣的可以了解一下
    2023-05-05
  • pygame实现弹力球及其变速效果

    pygame实现弹力球及其变速效果

    这篇文章主要为大家详细介绍了pygame实现弹力球及其变速效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • PyQt QListWidget修改列表项item的行高方法

    PyQt QListWidget修改列表项item的行高方法

    今天小编就为大家分享一篇PyQt QListWidget修改列表项item的行高方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-06-06
  • 解决Django layui {{}}冲突的问题

    解决Django layui {{}}冲突的问题

    今天小编就为大家分享一篇解决Django layui {{}}冲突的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • Python数据分析 Numpy 的使用方法

    Python数据分析 Numpy 的使用方法

    这篇文章主要介绍了Python数据分析 Numpy 的使用方法,Numpy 是一个Python扩展库,专门做科学计算,也是大部分Python科学计算库的基础,关于其的使用方法,需要的小伙伴可以参考下面文章内容
    2022-05-05
  • Python 查看数据类型与格式

    Python 查看数据类型与格式

    这篇文章主要介绍了Python 查看数据类型与格式方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • Python操作RabbitMQ服务器实现消息队列的路由功能

    Python操作RabbitMQ服务器实现消息队列的路由功能

    RabbitMQ是一个消息队列服务器,这里我们针对Python+Pika+RabbitMQ的服务器端环境,来看一下如何使用Python操作RabbitMQ服务器实现消息队列的路由功能
    2016-06-06
  • Django 实现图片上传和下载功能

    Django 实现图片上传和下载功能

    这篇文章主要介绍了Django 如何实现图片上传和下载功能,帮助大家更好的理解和使用django框架,感兴趣的朋友可以了解下
    2020-12-12
  • pytest解读一次请求多个fixtures及多次请求

    pytest解读一次请求多个fixtures及多次请求

    这篇文章主要为大家介绍了一次请求多个fixtures,以及fixtures被多次请求的pytest官方解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 用Python实现屏幕截图详解

    用Python实现屏幕截图详解

    大家好,本篇文章主要讲的是用Python实现屏幕截图详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01

最新评论