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将html转成PDF的实现代码(包含中文)
Mar 04 Python
python实现DNS正向查询、反向查询的例子
Apr 25 Python
Python中的错误和异常处理简单操作示例【try-except用法】
Jul 25 Python
Python元字符的用法实例解析
Jan 17 Python
Python中GeoJson和bokeh-1的使用讲解
Jan 03 Python
python 实现读取一个excel多个sheet表并合并的方法
Feb 12 Python
python针对mysql数据库的连接、查询、更新、删除操作示例
Sep 11 Python
Python装饰器原理与基本用法分析
Jan 07 Python
如何让PyQt5中QWebEngineView与JavaScript交互
Oct 21 Python
PyCharm Community安装与配置的详细教程
Nov 24 Python
Django项目在pycharm新建的步骤方法
Mar 02 Python
Python集合的基础操作
Nov 01 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
如何修改yii2.0自带的user表为其它的表
2017/08/01 PHP
php利用ZipArchive类操作文件的实例
2020/01/21 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
javascript DOM编程实例(智播客学习)
2009/11/23 Javascript
JavaScript转换农历类实现及调用方法
2013/01/27 Javascript
改变文件域的样式实现思路同时兼容ie、firefox
2013/10/23 Javascript
一个不错的仿携程自定义数据下拉选择select
2014/09/01 Javascript
PHP使用方法重载实现动态创建属性的get和set方法
2014/11/17 Javascript
浅谈JavaScript的Polymer框架中的behaviors对象
2015/07/29 Javascript
AngularJS基础 ng-mousemove 指令简单示例
2016/08/02 Javascript
AngularJS页面传参的5种方式
2017/04/01 Javascript
详解如何使用PM2将Node.js的集群变得更加容易
2017/11/15 Javascript
vue组件的写法汇总
2018/04/12 Javascript
vue拖拽排序插件vuedraggable使用方法详解
2020/08/21 Javascript
推荐15个最好用的JavaScript代码压缩工具
2019/02/13 Javascript
Node对CommonJS的模块规范
2019/11/06 Javascript
Ajax获取node服务器数据的完整步骤
2020/09/20 Javascript
js canvas实现五子棋小游戏
2021/01/22 Javascript
[50:04]DOTA2上海特级锦标赛D组小组赛#2 Liquid VS VP第二局
2016/02/28 DOTA
详解python中requirements.txt的一切
2017/03/03 Python
python 将字符串转换成字典dict的各种方式总结
2018/03/23 Python
python使用代理ip访问网站的实例
2018/05/07 Python
Python选择网卡发包及接收数据包
2019/04/04 Python
Python定义函数功能与用法实例详解
2019/04/08 Python
Python Django 简单分页的实现代码解析
2019/08/21 Python
Python Django 前后端分离 API的方法
2019/08/28 Python
Python把图片转化为pdf代码实例
2020/07/28 Python
python文件排序的方法总结
2020/09/13 Python
Python基于内置函数type创建新类型
2020/10/22 Python
台湾百利市购物中心:e-Payless
2017/08/16 全球购物
新闻编辑自荐信
2013/11/03 职场文书
报纸媒体创意广告词
2014/03/17 职场文书
春节请假条
2014/04/11 职场文书
《画杨桃》教学反思
2014/04/13 职场文书
中药学专业毕业生推荐信
2014/07/10 职场文书
详解nginx进程锁的实现
2021/06/14 Servers