PyQt5中多线程模块QThread使用方法的实现


Posted in Python onJanuary 31, 2020

本文主要讲解使用多线程模块QThread解决PyQt界面程序唉执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题。用户使用工具过程中出现这些问题时会误以为程序出错,从而把程序关闭。这样,导致工具的用户使用体验不好。下面我们通过模拟上述出现的问题并讲述使用多线程QThread模块解决此类问题的方法。

PyQt程序卡顿和无法实时显示问题现象

使用PyQt界面程序,点击运行按钮后,程序在显示框中每秒打印1个数字。程序代码如下:

# -*- coding: utf-8 -*-

import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow
from QThread_Example_UI import Ui_Form

class MyMainForm(QMainWindow, Ui_Form):
  def __init__(self, parent=None):
    super(MyMainForm, self).__init__(parent)
    self.setupUi(self)
    self.runButton.clicked.connect(self.display)

  def display(self):
    for i in range(20):
      time.sleep(1)
      self.listWidget.addItem(str(i))

if __name__ == "__main__":
  app = QApplication(sys.argv)
  myWin = MyMainForm()
  myWin.show()
  sys.exit(app.exec_())

程序运行过程结果如下(点击Run按钮后界面出现 未响应 字样 同时程序也没有出现每隔1秒打印1个数字,实际结果是循环结束后20个数字一同展示):

PyQt5中多线程模块QThread使用方法的实现 

PyQt5中多线程模块QThread使用方法的实现

问题分析

上述实现的GUI程序都是单线程运行,对于需要执行一个特别耗时的操作时就会出现该问题现象。要解决这种问题可以考虑使用多线程模块QThread。

多线程模块QThread基本原理

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

class Thread(QThread):
  def __init__(self):
    super(Thread,self).__init__()
  def run(self):
    #

接下来创建一个新的线程

thread = Thread()
thread.start()

可以看出,PyQt的线程使用非常简单,建立一个自定义的类(如Thread),自我继承自QThread ,并实现其run()方法即可。在使用线程时可以直接得到Thread实例,调用其start()函数即可启动线程,线程启动之后,会自动调用其实现的run()的函数,该方法就是线程的执行函数 。

业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了,QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执行一段代码进行资源的初始化和释放操作,更灵活的使用方法是,在自定义的QThread实例中自定义信号,并将信号连接到指定的槽函数,当满足一定的业务条件时发射此信号。

QThread类中的常用方法

  start():启动线程

  wait():阻止线程,直到满足如下条件之一

(1)与此QThread对象关联的线程已完成执行(即从run返回时),如果线程完成执行,此函数返回True,如果线程尚未启动,也返回True

(2)等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False

  sleep():强制当前线程睡眠多少秒

QThread类中的常用信号

  started: 在开始执行run函数之前,从相关线程发射此信号

finished:当程序完成业务逻辑时,从相关线程发射此信号

使用QThread重新实现程序解决问题

先继承QThread类并重新实现其中的run()函数,也就是说把耗时的操作放入run()函数中。代码如下:

# -*- coding: utf-8 -*-

import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow
from QThread_Example_UI import Ui_Form

class MyMainForm(QMainWindow, Ui_Form):
  def __init__(self, parent=None):
    super(MyMainForm, self).__init__(parent)
    self.setupUi(self)
    # 实例化线程对象
    self.work = WorkThread()
    self.runButton.clicked.connect(self.execute)

  def execute(self):
    # 启动线程
    self.work.start()
    # 线程自定义信号连接的槽函数
    self.work.trigger.connect(self.display)

  def display(self,str):
    # 由于自定义信号时自动传递一个字符串参数,所以在这个槽函数中要接受一个参数
    self.listWidget.addItem(str)

class WorkThread(QThread):
  # 自定义信号对象。参数str就代表这个信号可以传一个字符串
  trigger = pyqtSignal(str)

  def __int__(self):
    # 初始化函数
    super(WorkThread, self).__init__()

  def run(self):
    #重写线程执行的run函数
    #触发自定义信号
    for i in range(20):
      time.sleep(1)
      # 通过自定义信号把待显示的字符串传递给槽函数
      self.trigger.emit(str(i))

if __name__ == "__main__":
  app = QApplication(sys.argv)
  myWin = MyMainForm()
  myWin.show()
  sys.exit(app.exec_())

程序运行结果如下(实现了每隔1秒打印1个数字):

PyQt5中多线程模块QThread使用方法的实现

小结

如果你实现的工具需要执行特别耗时的操作,可以参考使用本文多线程QThread处理方法实现。当然,工具实际实现过程中的场景会比这复杂。比如,你的输出并不是有固定时间间隔输出的文本框,可以尝试使用多次self.trigger.emit(str)方法进行操作。

附录

1、使用pyuic5转换界面.ui程序后的QThread_Example_UI.py代码如下:

# -*- coding: utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
  def setupUi(self, Form):
    Form.setObjectName("Form")
    Form.resize(498, 331)
    self.runButton = QtWidgets.QPushButton(Form)
    self.runButton.setGeometry(QtCore.QRect(190, 30, 75, 23))
    self.runButton.setObjectName("runButton")
    self.listWidget = QtWidgets.QListWidget(Form)
    self.listWidget.setGeometry(QtCore.QRect(30, 70, 431, 192))
    self.listWidget.setObjectName("listWidget")

    self.retranslateUi(Form)
    QtCore.QMetaObject.connectSlotsByName(Form)

  def retranslateUi(self, Form):
    _translate = QtCore.QCoreApplication.translate
    Form.setWindowTitle(_translate("Form", "Qthread Example"))
    self.runButton.setText(_translate("Form", "Run"))

 2、Qtdesigner设计的界面源程序代码QThread_Example_UI.ui如下:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
 <property name="geometry">
  <rect>
  <x>0</x>
  <y>0</y>
  <width>498</width>
  <height>331</height>
  </rect>
 </property>
 <property name="windowTitle">
  <string>Qthread Example</string>
 </property>
 <widget class="QPushButton" name="runButton">
  <property name="geometry">
  <rect>
   <x>190</x>
   <y>30</y>
   <width>75</width>
   <height>23</height>
  </rect>
  </property>
  <property name="text">
  <string>Run</string>
  </property>
 </widget>
 <widget class="QListWidget" name="listWidget">
  <property name="geometry">
  <rect>
   <x>30</x>
   <y>70</y>
   <width>431</width>
   <height>192</height>
  </rect>
  </property>
 </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
py中的目录与文件判别代码
Jul 16 Python
在python中的socket模块使用代理实例
May 29 Python
让 python 命令行也可以自动补全
Nov 30 Python
python简单的函数定义和用法实例
May 07 Python
Python的Flask框架中的Jinja2模板引擎学习教程
Jun 30 Python
Python 中的Selenium异常处理实例代码
May 03 Python
python 编码规范整理
May 05 Python
Python封装成可带参数的EXE安装包实例
Aug 24 Python
Flask框架 CSRF 保护实现方法详解
Oct 30 Python
利用python中的matplotlib打印混淆矩阵实例
Jun 16 Python
Python venv虚拟环境配置过程解析
Jul 08 Python
10个顶级Python实用库推荐
Mar 04 Python
浅谈python之自动化运维(Paramiko)
Jan 31 #Python
为什么黑客都用python(123个黑客必备的Python工具)
Jan 31 #Python
Win10里python3创建虚拟环境的步骤
Jan 31 #Python
python判断链表是否有环的实例代码
Jan 31 #Python
python爬取王者荣耀全皮肤的简单实现代码
Jan 31 #Python
Python进阶之迭代器与迭代器切片教程
Jan 29 #Python
Python列表list操作相关知识小结
Jan 29 #Python
You might like
php SQL之where语句生成器
2009/03/24 PHP
使用php计算排列组合的方法
2013/11/13 PHP
php实现批量下载百度云盘文件例子分享
2014/04/10 PHP
PHPer 需要了解的 5 个 Composer 小技巧
2014/08/18 PHP
PHP提示Cannot modify header information - headers already sent by解决方法
2014/09/22 PHP
PHP+Mysql+jQuery文件下载次数统计实例讲解
2015/10/10 PHP
ThinkPHP模板Volist标签嵌套循环输出多维数组的方法
2016/03/23 PHP
PHP简单实现遍历目录下特定文件的方法小结
2017/05/22 PHP
使用PHP开发留言板功能
2019/11/19 PHP
jQuery 改变CSS样式基础代码
2010/02/11 Javascript
js拦截alert对话框另类应用
2013/01/16 Javascript
Jquery实现带动画效果的经典二级导航菜单
2013/03/22 Javascript
javascript基础语法学习笔记
2016/01/04 Javascript
用JS实现轮播图效果(二)
2016/06/26 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
2016/10/31 Javascript
Web前端框架bootstrap实战【第一次接触使用】
2016/12/28 Javascript
解决Mac node版本升级失败的问题
2018/05/16 Javascript
vue生成token并保存到本地存储中
2018/07/17 Javascript
解决微信浏览器缓存站点入口文件(IIS部署Vue项目)
2019/06/17 Javascript
JavaScript学习教程之cookie与webstorage
2019/06/23 Javascript
python实现迭代法求方程组的根过程解析
2019/11/25 Javascript
Python中使用动态变量名的方法
2014/05/06 Python
Python中特殊函数集锦
2015/07/27 Python
解决uWSGI的编码问题详解
2017/03/24 Python
对Tensorflow中的矩阵运算函数详解
2018/07/27 Python
python3反转字符串的3种方法(小结)
2019/11/07 Python
pandas之分组groupby()的使用整理与总结
2020/06/18 Python
利用CSS3实现文本框的清除按钮相关的一些效果
2015/06/23 HTML / CSS
HTML5的Geolocation地理位置定位API使用教程
2016/05/12 HTML / CSS
德国高性价比网上药店:medpex
2017/07/09 全球购物
Nike德国官网:Nike.com (DE)
2018/11/13 全球购物
意大利时尚精品店:Nugnes 1920
2020/02/10 全球购物
90后毕业生的求职信范文
2013/09/21 职场文书
信息合作协议书
2014/10/09 职场文书
信息技术教研组工作总结
2015/08/13 职场文书
MySQL深分页问题解决思路
2022/12/24 MySQL