PyQt子线程处理业务事件

在PyQt中是不推荐使用UI主线程来处理耗时操作的,会造成窗口组件阻塞。耗时操作一般放在子线程中。子线程处理完成后,可能需要更新窗口组件,但是PyQt不推荐使用子线程来更新主线程(也不是不能更新),这就用到了信号槽机制来更新主线程。

  • 在QObject的一个子类中创建一个信号(PyQt5.QtCore.pyqtSignal)属性
  • 将这个信号属性和其他类中的函数绑定,绑定的这个函数叫做整个信号的槽函数。一个信号可以和多个槽函数绑定。
  • 该信号发出时,就会调用对应的槽函数

可能会有疑问,槽函数被执行时所在的线程和发送信号的线程是不是同一个?

需要注意,信号一定义在QObject或其子类中。调用该属性的emit方法发出信号后,和该信号绑定的槽函数都将要被调用,但是调用的线程并不一定是发送信号的这个线程,这和PyQt中的线程亲和性(Thread Affinity)有关。

线程亲和性(Thread Affinity)

在 PyQt 中,一个对象可以被移动到不同的线程中,但一个对象在同一时刻只能属于一个线程。这是因为 Qt 使用线程亲和性(Thread Affinity)的概念来管理对象所属的线程。

每个 Qt 对象都与一个特定的线程相关联,即它的线程亲和性。对象的线程亲和性决定了该对象的槽函数是在哪个线程中执行。默认情况下,对象在创建时会与创建它的线程相关联,但可以使用 moveToThread 方法将对象移动到另一个线程中。

错误示例:

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QFileDialog
from PyQt5.QtCore import QThread,pyqtSignal,QObject
import sys, threadingclass MyWindow(QMainWindow):def __init__(self, parent=None):super(MyWindow, self).__init__(parent)self.button = QPushButton('Hi')self.button.clicked.connect(self.on_click)self.setCentralWidget(self.button)def on_click(self):print("on_click",threading.current_thread().name)self.thread = MyThread(self)self.thread.start()def set_text(self,file_name):print("setText",threading.current_thread().name)self.button.setText(file_name)class MyThread(QThread):def __init__(self,mv:QMainWindow) -> None:super().__init__(None)self.mv = mvdef run(self):print('run',threading.current_thread().name)QThread.sleep(5)self.mv.set_text("Hello World")if __name__ == '__main__':app = QApplication([])window = MyWindow()window.show()sys.exit(app.exec_())

输出结果:

on_click MainThread
run Dummy-1
setText Dummy-1   //子线程更新UI,不推荐

使用信号槽机制

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QFileDialog
from PyQt5.QtCore import QThread,pyqtSignal,QObject
import sys, threading
class MyWindow(QMainWindow):def __init__(self, parent=None):super(MyWindow, self).__init__(parent)self.button = QPushButton('Hi')self.button.clicked.connect(self.on_click)self.setCentralWidget(self.button)def on_click(self):print("on_click",threading.current_thread().name)self.thread = MyThread(self)self.thread.pyqtSignal.connect(self.set_text)self.thread.start()def set_text(self,file_name):print("setText",threading.current_thread().name)self.button.setText(file_name)class MyThread(QThread):pyqtSignal =  pyqtSignal(str)def __init__(self,mv:QMainWindow) -> None:super().__init__(None)self.mv = mvdef run(self):print('run',threading.current_thread().name)QThread.sleep(5)self.pyqtSignal.emit("Hello World")if __name__ == '__main__':app = QApplication([])window = MyWindow()window.show()sys.exit(app.exec_())

输出结果:

on_click MainThread
run Dummy-1
setText MainThread //更新UI时,执行的线程为主线程

setText槽函数为什么会被主函数执行,就是因为线程亲和性,槽函数所在对象和MainThread绑定,当然会被主线程所执行。

但是这种将事务直接写在run,PyQt5是不推荐的,正确写法如下

创建一个类集成QObject,来做业务的处理。并将这个对象和新创建的线程通过moveToThread绑定,作为这个对象的亲和线程。将QThread的started信号和这个业务事件绑定。线程启动,发送started信号,业务对象开始处理业务,完成之后发送信号给主线程槽函数。

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QFileDialog
from PyQt5.QtCore import QThread,pyqtSignal,QObject
import sys, threading
class MyWindow(QMainWindow):def __init__(self, parent=None):super(MyWindow, self).__init__(parent)self.button = QPushButton('Hi')self.button.clicked.connect(self.on_click)self.setCentralWidget(self.button)def on_click(self):print("on_click",threading.current_thread().name)self.thread = QThread()self.myHander = MyHandler()self.myHander.moveToThread(self.thread)self.myHander.pyqtSignal.connect(self.set_text)self.thread.started.connect(self.myHander.handle)self.thread.start()def set_text(self,file_name):print("setText",threading.current_thread().name)self.button.setText(file_name)
class MyHandler(QObject):pyqtSignal =  pyqtSignal(str)def handle(self):print('handle',threading.current_thread().name)self.pyqtSignal.emit("Hello World")if __name__ == '__main__':app = QApplication([])window = MyWindow()window.show()sys.exit(app.exec_())

子线程中调用QFileDialog

如果在子线程中调用了QFileDialog窗口选择文件,QFileDialog窗口出现后几秒后程序会崩溃,代码如下

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QFileDialog
from PyQt5.QtCore import QThread,pyqtSignal,QObject
import sys, threading
class MyWindow(QMainWindow):def __init__(self, parent=None):super(MyWindow, self).__init__(parent)self.button = QPushButton('Hi')self.button.clicked.connect(self.on_click)self.setCentralWidget(self.button)def on_click(self):print("on_click",threading.current_thread().name)self.thread = MyThread(self)self.thread.pyqtSignal.connect(self.set_text)self.thread.start()def set_text(self,file_name):print("setText",threading.current_thread().name)self.button.setText(file_name)class MyThread(QThread):pyqtSignal =  pyqtSignal(str)def __init__(self,mv:QMainWindow) -> None:super().__init__(None)self.mv = mvdef run(self):print('run',threading.current_thread().name)file_name = QFileDialog.getOpenFileName(self.mv, '选择文件', './', 'Excel files(*.xlsx , *.xls)')print(file_name)self.pyqtSignal.emit("Hello World")if __name__ == '__main__':app = QApplication([])window = MyWindow()window.show()sys.exit(app.exec_())

输出结果:

on_click MainThread
run Dummy-1
QObject::setParent: Cannot set parent, new parent is in a different thread
CoCreateInstance failed (操作成功完成。)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x21fb451d190), parent's thread is QThread(0x21fb443b430), current thread is MyThread(0x21fb8788df0)
CoCreateInstance failed (操作成功完成。)
QObject::startTimer: Timers cannot be started from another thread

问题原因

PyQt中,必须在主线程中来创建子对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/663741.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 常用排序算法(冒泡排序 插入排序 选择排序 快速排序 归并排序 堆排序)

C中常用的排序算法包括以下几种: 冒泡排序(Bubble Sort):通过不断交换相邻的元素,将最大的元素逐渐向数组的末尾冒泡。时间复杂度为O(n^2)。 插入排序(Insertion Sort):将待排序元素…

【论文解读】Collaboration Helps Camera Overtake LiDAR in 3D Detection

CoCa3D 摘要引言Collaborative Camera-Only 3D DetectionCollaborative depth estimationCollaborative detection feature learning 实验结论和局限 摘要 与基于 LiDAR 的检测系统相比,仅相机 3D 检测提供了一种经济的解决方案,具有简单的配置来定位 3…

MySQL进阶45讲【11】怎么更好地给字符串字段加索引?

1 前言 现在,几乎所有的系统都支持邮箱登录,如何在邮箱这样的字段上建立合理的索引,是我们今天要讨论的问题。 假设,现在维护一个支持邮箱登录的系统,用户表是这么定义的: mysql> create table SUser…

删除有序数组中的重复项 II[中等]

优质博文:IT-BLOG-CN 一、题目 给你一个有序数组nums,请你原地删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额…

HarmonyOS ArkTS Button基本使用(十七)

HarmonyOS ArkTS是一种应用于鸿蒙系统的应用开发语言,它在TypeScript的基础上,扩展了声明式UI、状态管理等能力。在HarmonyOS中,Button是一种常用的组件,用于实现页面间的跳转和交互。下面详细介绍HarmonyOS ArkTS中Button的基本使…

jvm基础篇之垃圾回收[2](垃圾回收算法)

文章目录 版权声明垃圾回收算法核心思想垃圾回收算法的历史垃圾回收算法的评价标准垃圾分类算法分类标记清除算法核心思想标记清除算法优缺点 复制算法核心思想完整案例复制算法的优缺点 标记整理算法核心思想标记整理算法优缺点 分代垃圾回收算法arthas查看分代内存情况核心思…

stm32软件安装以及创建工程

文章目录 前言一、软件安装软件破解 二、创建工程三、创建项目创建组配置启动文件添加到组 为项目添加头文件路径创建源文件(main函数文件)使用寄存器配置引脚拼接好STLINK与stm32最小电路板的接线编写程序配置STLink下载程序配置寄存器配置13号端口&…

用户体验优化:HubSpot的秘密武器

在当今数字化市场中,提升用户体验已经成为企业成功的关键因素之一。HubSpot,作为一款领先的营销自动化工具,不仅在推动销售业绩上表现出色,同时通过其独特的策略也致力于提升用户体验。运营坛将深入探讨HubSpot是如何通过个性化推…

Leetcode—41. 缺失的第一个正数【困难】

2024每日刷题&#xff08;一零九&#xff09; Leetcode—41. 缺失的第一个正数 实现代码 class Solution { public:int firstMissingPositive(vector<int>& nums) {int n nums.size();// nums[i] i 1;// nums[i] - 1 i;// nums[nums[i] - 1] nums[i];for(int …

能源管理新高度DI/DO/CAN/RS485/USB网关助力二次开发

能源管理领域正在寻求更为智能化和高效的解决方案。一款集成了先进ARM架构处理器的边缘计算能源储能网关应运而生&#xff0c;以其卓越的性能和丰富的接口功能吸引了众多行业用户的关注。 这款网关不仅配备有常规的数字输入&#xff08;DI&#xff09;、数字输出&#xff08;DO…

LeetCode 1686. 石子游戏 VI【排序,贪心】【Py3,Go】2000

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

【简便方法和积累】pytest 单元测试框架中便捷安装插件和执行问题

又来进步一点点~~~ 背景&#xff1a;之前写了两篇关于pytest单元测试框架的文章&#xff0c;本篇内容对之前的做一个补充 一、pytest插件&#xff1a; pytest 有非常多的插件&#xff0c;很方便&#xff0c;以下为插件举例&#xff1a; pytest&#xff0c;pytest-html&#x…

CSS 选择器优先级详解及实例演示

在CSS样式设计中&#xff0c;不同的选择器具有不同的优先级&#xff0c;当多个样式应用于同一元素时&#xff0c;优先级较高的规则将覆盖优先级较低的规则。以下是对CSS选择器优先级的详细说明&#xff0c;并通过实例进行演示&#xff1a; 1. 内联样式 (Inline Styles) 内联样…

新手从零开始学习数学建模论文写作(美赛论文临时抱佛脚篇)

本文记录于数学建模老哥视频的学习过程中。b站视频&#xff1a;http://【【零基础教程】老哥&#xff1a;数学建模算法、编程、写作和获奖指南全流程培训&#xff01;】https://www.bilibili.com/video/BV1kC4y1a7Ee?p50&vd_sourceff53a726c62f94eda5f615bd4a62c458 目录…

树型结构构建,模糊查询,过滤

一、前言 1、最近在做甘特图&#xff0c;有些需求和树型结构要求很大&#xff0c;看的是 pingCode&#xff0c;有搜索 2、还有抽取一部分树型结构的&#xff0c;如下是抽取上面的结构类型为需求的&#xff0c;重新组成树型 二、构建多颗树型结构 1、某些业务下&#xff0c;从…

C语言指针的几种用途

先看题目&#xff0c;写一个fun函数&#xff0c;统计一个字符串中某个字符出现的次数&#xff0c;以及这个字符第一次出现的位置。 看起来很简单&#xff0c;似乎几行就可以搞定&#xff0c;但是写出来之后&#xff0c;才发现代码怎么这么长&#xff01;程序里多处使用了指针&…

069:vue中EventBus的使用方法(图文示例)

第069个 查看专栏目录: VUE ------ element UI 本文章目录 示例背景示例效果图示例源代码父组件&#xff1a;子组件A&#xff1a;子组件B&#xff1a;eventbus/index.js&#xff1a; EventBus的基本使用方法&#xff1a; 示例背景 在Vue中&#xff0c;使用EventBus可以实现组件…

爬虫(二)使用urllib爬取百度贴吧的数据

下一期我就不用urllib来抓取数据了&#xff0c;因为urllib现在已经很少人用&#xff0c;大部分人用得是requests&#xff0c;requests也是基于底层urllib的一个模块。 首先我先来讲一下关于如何使用动态的UA&#xff01; 动态UA就是指在自己创建的一个列表里随机选择一个UA当做…

Java开发工具 IntelliJ IDEA 2023中文

IntelliJ IDEA 2023是一款强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;适用于多种编程语言&#xff0c;包括Java、Python、Kotlin等。它提供了许多特色功能&#xff0c;以提高开发效率和代码质量。 Java开发工具 IntelliJ IDEA 2023中文 以下是一些IntelliJ ID…

探索ChatGPT:AI技术的新篇章与人类的共舞

### 探索ChatGPT&#xff1a;AI技术的新篇章与人类的共舞 2023年&#xff0c;ChatGPT成为全球科技领域的热点&#xff0c;标志着生成式人工智能&#xff08;AI&#xff09;技术的新时代。ChatGPT不仅加速了人工智能的发展步伐&#xff0c;也促进了人与AI之间的交互方式&#x…