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 相关文章推荐
python字符串加密解密的三种方法分享(base64 win32com)
Jan 19 Python
Python标准库urllib2的一些使用细节总结
Mar 16 Python
python清理子进程机制剖析
Nov 23 Python
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
Dec 20 Python
解决Python 异常TypeError: cannot concatenate 'str' and 'int' objects
Apr 08 Python
Python基于smtplib协议实现发送邮件
Jun 03 Python
python为什么会环境变量设置不成功
Jun 23 Python
Python定义一个Actor任务
Jul 29 Python
使用Python操作MySQL的小技巧
Sep 10 Python
Pycharm安装第三方库失败解决方案
Nov 17 Python
Django一小时写出账号密码管理系统
Apr 29 Python
Python 读取千万级数据自动写入 MySQL 数据库
Jun 28 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
PHP7新增运算符用法实例分析
2016/09/26 PHP
解决 firefox 不支持 document.all的方法
2007/03/12 Javascript
Javascript new Date().valueOf()的作用与时间戳由来详解
2013/04/24 Javascript
JS截取字符串常用方法整理及使用示例
2013/10/18 Javascript
简单的两种Extjs formpanel加载数据的方式
2013/11/09 Javascript
JavaScript显示当前文档最后修改日期的方法
2015/03/19 Javascript
jQuery实现自动调整字体大小的方法
2015/06/15 Javascript
基于JavaScript实现移动端TAB触屏切换效果
2015/10/20 Javascript
JavaScript获取各大浏览器信息图示
2015/11/20 Javascript
js判断登陆用户名及密码是否为空的简单实例
2016/05/16 Javascript
微信公众平台开发教程(四) 实例入门:机器人回复(附源码)
2016/12/02 Javascript
详解探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用
2017/06/16 Javascript
微信小程序 input表单与redio及下拉列表的使用实例
2017/09/20 Javascript
详解vue-cli项目中的proxyTable跨域问题小结
2018/02/09 Javascript
vue中动态绑定表单元素的属性方法
2018/02/23 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
2018/09/15 Javascript
利用Electron简单撸一个Markdown编辑器的方法
2019/06/10 Javascript
JQuery发送ajax请求时中文乱码问题解决
2019/11/14 jQuery
Node.js文本文件BOM头的去除方法
2020/11/22 Javascript
[56:57]LGD vs VP 2019DOTA2国际邀请赛淘汰赛 胜者组赛BO3 第一场 8.20.mp4
2019/08/22 DOTA
解析Python中的异常处理
2015/04/28 Python
python使用KNN算法手写体识别
2018/02/01 Python
tensorflow 获取变量&amp;打印权值的实例讲解
2018/06/14 Python
python多线程抽象编程模型详解
2019/03/20 Python
Python面向对象程序设计类变量与成员变量、类方法与成员方法用法分析
2019/04/12 Python
Flask框架学习笔记之使用Flask实现表单开发详解
2019/08/12 Python
如何在mac环境中用python处理protobuf
2019/12/25 Python
Python日志:自定义输出字段 json格式输出方式
2020/04/27 Python
css3实现一款模仿iphone样式的注册表单
2013/03/20 HTML / CSS
英文版餐饮运营管理求职信
2013/11/06 职场文书
金融专业毕业生推荐信
2013/11/26 职场文书
护士辞职信范文
2014/01/19 职场文书
乡镇干部十八大感言
2014/02/17 职场文书
销售员试用期自我评价
2014/09/15 职场文书
员工手册编写范本
2015/05/14 职场文书
后天观后感
2015/06/08 职场文书