目录

前言

PyQt线程科学用法

非科学用法样例

科学用法

线程类

线程通信

线程类在主界面实例化与使用

开启线程

补充(信号的方式实现线程双向通信):

线程类

线程实例化与开启线程挂在后台

发送信号,线程处理数据

结语

参考文章


前言

本文主要讲解PyQt使用多线程模块QThread解决PyQt界面程序执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题。有时候,我们写的程序界面出现未响应,是因为把需要长时间运行的代码放在了主线程,阻塞了事件循环。

QtCore.QThread是一个管理线程的类,当我们使用其构造函数的时候,便新建了一个线程。这里要强调,QThread是一个线程管理器,不要把业务逻辑放在这个类里面Qt的作者已经多次批评继承QThread类来实现业务逻辑的做法。然而我在网上看了很多资料或者博客,很大一部分都是继承QThread实现实现业务逻辑...当然也有很多科学使用的教程,不过相对来说较少。我今天就把我的科学使用PyQt多线程,心里路程分享出来,供大家参考。

PyQt线程科学用法

非科学用法样例

上面介绍了非科学用法,是直接继承QThread在里面写业务。那么网上很多教程也就是这样的....

class Thread(QThread):def __init__(self):super(Thread,self).__init__()def run(self):#业务逻辑代码#创建一个新的线程
thread = Thread()
thread.start()

科学用法

Qt的作者已经多次批评继承QThread类来实现业务逻辑的做法,那么怎么使用才是科学使用PyQt多线程呢?答案如下:

自己定义一个线程类,这个类要继承QObject在里面实现线程的相关业务逻辑,同时在主界面里实例化这个线程类,然后用moveToThread方法移动到QThread管理。

# fixme PyQt线程科学用法
self.thread = QThread()
#实例外线程对象 workThread是自己写的线程类,后面会贴出来
self.work_thread = workThread()
# 把实例化的线程用moveToThread移到QThread管理
self.work_thread.moveToThread(self.thread)

线程类

首先写好自己的线程类,实现业务功能 ,因为我这里是由于图像处理耗时,直接把原来的代码贴过来,稍加修改的。要注意的是线程之间的通信,以及是否会有资源竞争等情况。 QtCore.Signal和QtCore.pyqtSignal是一样的,我这里这样写是开源的labelme是这样的,我就采用原样写法。都是信号定义的方式。

class workThread(QObject):#图像处理完成信号to_show_img_signal = QtCore.Signal(QtGui.QImage)def __init__(self):super(workThread, self).__init__()def work(self):global imageDataglobal mask_list"""省略图像处理相关代码"""#处理完成发送信号self.to_show_img_signal.emit(qimage)

线程通信

线程通信的方式:全局变量,消息传递(PyQt信号槽机制)

我这里采用的是全局变量,在Mianwindow类和workThread类之外定义了两个全局变量,在两个类使用到改变量都需要加global,才能实现全局效果。

大家可能会疑问,我为什么要使用全局变量,作为线程通信的方式,为什么不用 信号槽?我最开始也是想用信号槽,也是这么做的,但是会报错。因为我这里使用线程的同时需要主界面发送图像数据给子线程,子线程处理完毕后,发送给主界面显示。也就是线程双向通信

信号槽机制:子线程向主页面发送信号以及数据确实很方便。但是主界面发送数据给子线程就会出问题。使用信号槽程序会出现如下错误,获取不到信号的connect方法。

'PyQt5.QtCore.pyqtSignal' object has no attribute 'connect

上面的报错,我以前也遇到过,也有解决方案,但是这次问题和这个报错不沾边的....

信号的正确定义和使用以及上面报错解决方案:https://memory-qianxiao.blog.csdn.net/article/details/105754667

所以我就猜想信号槽是子线程向主界面发送信号使用的,如果要主界面发送数据给子线程,需要其他方法实现。

线程类在主界面实例化与使用

这里贴出来的代码是我代码里面线程使用精简化后的,为了方便大家理解,同时写了注释,比较核心的几行代码就是init里面那几行。这个是科学使用PyQt线程的模板了。尤其注意线程完毕时,需要关闭线程, 线程不会自己关闭的。

class MainWindow(QtWidgets.QMainWindow):def __init__(self):# fixme PyQt线程科学用法self.thread = QThread()# 实例化线程类self.work_thread = workThread()#moveToThread方法把实例化线程移到Thread管理self.work_thread.moveToThread(self.thread)# 线程开始执行之前,从相关线程发射信号self.thread.started.connect(self.work_thread.work)#接收子线程信号发来的数据self.work_thread.to_show_img_signal.connect(self.show_img_in_labelme)# 线程执行完成关闭线程self.thread.finished.connect(self.threadStop)def threadStart(self):# 开启线程self.thread.start()def threadStop(self):# 退出线程self.thread.quit()#接收线程数据的槽函数def show_img_in_labelme(self, qimage):self.onNewBrightnessContrast(qimage)

开启线程

我们这里需要特别注意,线程的开启应在主界面里面,当有需要处理耗时操作的时候,就主动开启一个线程,处理耗时任务,处理完毕,检测线程执行完毕,就需要关闭。上面已经定义了开启方法。所以只需要在用到的地方调用函数即可

def eraser_or_brush(self, coordinate):#全局变量global mask_list  global imageData#业务逻辑代码"""橡皮擦功能""""""笔刷功能(逆向橡皮擦)"""#主动开启一个线程self.thread.start()# 设置撤销按钮是否可用self.actions.undo.setEnabled(self.isHasMaskImage())

补充(信号的方式实现线程双向通信):

补充时间:20201年1月5日

上面的方式是基于全局变量,实现主线程和子线程的通信,子线程处理完毕向主线程发送信号。现在这里补充另一种写法思路:在主线程一开始(init初始化)就开启一个线程挂在后台,在有需要的的时候,就发送数据和信号,去让线程处理,处理完毕在发送信号给主线程

线程类

与上面的线程类不同的是这里线程处理方法多了两个参数一个信号。

from qtpy.QtCore import QObject, QThread
from qtpy import QtCore, QtGui
import cv2
import numpy as np
from . import utils
import timeclass ImageProcessingThread(QObject):#处理完毕图像数据信号to_show_img_signal = QtCore.Signal(QtGui.QImage)#线程接收参数信号to_start_image_process_thread_signal = QtCore.Signal(bytes, list)def __init__(self):super(ImageProcessingThread, self).__init__()#接收两个个参数的方法def work(self, imageData, mask_list):"""图像处理业务过程"""#处理完毕发送信号self.to_show_img_signal.emit(qimage)

线程实例化与开启线程挂在后台

class MainWindow(QtWidgets.QMainWindow):def __init__(self):# fixme PyQt线程科学用法self.thread = QThread()# 初始化线程类传参self.image_process_thread = ImageProcessingThread()self.image_process_thread.moveToThread(self.thread)# 连接槽函数self.image_process_thread.to_start_image_process_thread_signal.connect(self.image_process_thread.work)self.image_process_thread.to_show_img_signal.connect(self.show_img_in_labelme)# 开启线程 一直挂在后台self.thread.start()

发送信号,线程处理数据

def eraser_or_brush(self, coordinate):# 业务逻辑代码"""橡皮擦功能""""""笔刷功能(逆向橡皮擦)"""# 发送信号给线程,让线程开始工作self.image_process_thread.to_start_image_process_thread_signal.emit(self.imageData, self.mask_list)# 设置撤销按钮是否可用self.actions.undo.setEnabled(self.isHasMaskImage())

结语

希望我这篇文章对你有所帮助,希望三连,不胜感激~

多线程,多进程这一块其实还是挺难的,虽然代码很短,但是要具体实现某个复杂一点的功能,要控制好真的不的不容易~要花大量时间去采坑,去看别人的参考资料~而现在网上资料太多了,很多博主又是直接把转载或者把别人文章原样不动发表,让我们要花更多的时间去看找资料,真的很难受~

我的这篇文章主要是把我的心得体会与精简化的代码贴出来,供大家学习,相当于一个模版。大家少了采坑的过程,可能会对多线程理解还不够,不能理解我的一些做法,不过我会把我觉得有用参考文章给大家贴到后面。供大家去参考,理解。

参考文章

点击字体即可进入文章

  • 使用PyQt线程的正确姿势
  • pyqt5 的多线程(QThread)遇到的坑(一)
  • PyQt5开发学习(三)--使用moveToThread异步刷新UI
  • pyqt多线程moveToThread的使用

PyQt之科学使用线程处理耗时任务以及线程通信方法相关推荐

  1. 一文教会你什么线程安全以及如何实现线程安全

    title: 一文教会你什么线程安全以及如何实现线程安全 tags: 线程安全 Java并发 Java内存模型 synchronized volatile Lock 文章目录 一.线程安全的概念 二. ...

  2. 线程状态以及sleep yield wait join方法

    前言 在日常的开发过程中,我们通过会使用Thread.sleep模拟一个耗时的任务执行过程. 在深入理解这四个方法之前,首先对线程的状态进行理解阐述. 线程概念 线程是操作系统执行任务的基本单位,处理 ...

  3. python结束线程池正在运行的线程_python之线程与线程池

    #进程是资源分配的最小单位,线程是CPU调度的最小单位.每一个进程中至少有一个线程.#传统的不确切使用线程的程序称为只含有一个线程或单线程程序,而可以使用线程的程序被称为多线程程序,在程序中使用一个线 ...

  4. linux 线程_浅谈Linux线程模型

    Thread Basic 基础概念 线程是操作系统能够调度和执行的基本单位,在Linux中也被称之为轻量级进程.从定义中可以看出,线程它是操作系统的概念,在不同的操作系统中的实现是不同的,不过今天分享 ...

  5. future 线程报错后_线程池运用实例——一次错误的多线程程序设计以及修复过程...

    写在前面的话 写下这篇文章只为了回顾之前在实际工作中犯的一个极其二逼的错误,用我的经历来提示后来者,诸位程序大神,大牛,小牛们看到此文笑笑即可,轻拍轻拍... 1 背景 有这么一个需求,我们的系统(后 ...

  6. java 线程 通过interrupted_分析Java线程中断机制stop和interrupted的用法

    1.引言当我们点击某个杀毒软件的取消按钮来停止查杀病毒时,当我们在控制台敲入quit命令以结束某个后台服务时--都需要通过一个线程去取消另一个线程正在执行的任务.Java没有提供一种安全直接的方法来停 ...

  7. C#中的多线程-线程同步基础 (控制线程数量)

    同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具: 简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成 锁系统 构成 目的 跨进程? 速度 loc ...

  8. java 线程什么时候结束_java线程什么时候让出cpu?

    Thread.sleep(); sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep ...

  9. python两个线程交替打印_三线程按顺序交替打印ABC的四种方法

    建立三个线程A.B.C,A线程打印10次字母A,B线程打印10次字母B,C线程打印10次字母C,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印. 二.Synchroni ...

最新文章

  1. 圈钱的道路上廖翔从不缺席
  2. 给定2个字符串,如何计算变化(插入、删除、替换)?【levenshtein distance 算法】
  3. STM32L0开发——ADC多通道采集,IDE和IAR开发注意事项
  4. arch linux 安装xfce_树莓派安装ArchLinux+桌面环境
  5. linux sublime3 插件安装插件,手动安装sublimeText3插件
  6. Android activity启动模式
  7. python设置字体_Python实现文字特效的方法
  8. 总结30个CSS3选择器(转载)
  9. Kettle工具使用及总结
  10. java object 判断null_java判断object为null
  11. 一个系统管理员的自白
  12. 普通摄像头的数据输出格式YUV与mjpeg之间联系、DCT离散余弦变换去噪跟压缩(待补充)
  13. 2021泰迪杯数据分析技能赛B题解题思路分享
  14. Android常用对话框大全——Dialog
  15. 使用Metricbeat和Filebeat监控Nginx性能指标和日志
  16. Keystone认证服务详细操作流程
  17. TOJ 5138: 数字游戏
  18. C# task await 等待任务完成
  19. 广东工业大学计算机专业分流,2017年广东工业大学大类招生学生专业分流工作小组...
  20. 基于Java的奖学金评定管理系统

热门文章

  1. Python找出二维数组中某个元素索引,自定义函数
  2. 修改docker mysql容器端口的映射
  3. 计算机与打印机不在同一网段里,不在同一网段的计算机如何共享打印机电脑问题...
  4. 纯css制作三级下拉菜单
  5. 115 个 Java 面试题和答案——终极重点(下)
  6. ipconfig命令的几种使用方法
  7. UISlider 延迟调用touchesBegan,延迟调用valueChangeHandle。delay?
  8. 2021中国农业生产数字化研究报告 附下载
  9. 全球明星CEO的黄金法则
  10. mysql牛客刷题(SQL大厂面试真题)