详解PySide多线程,解决图形界面卡顿问题【Pyside/Qt专栏#2】

在了解pyside多线程之前,如果是初学者可以先去看看我的第一篇文章《超详细实例详解Python多线程》,文中有对多线程的概念进行详细介绍,如果有相关基础可直接跳过。

【Python】超详细实例讲解python多线程(threading模块)_python threading介绍-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Xiao_Liu_OvO/article/details/138716321?spm=1001.2014.3001.5501

1.为什么要使用多线程?

在制作图形界面时,只用一个线程很容易导致卡顿无响应,一旦主线程被阻塞,那么整个图形界面都会无法继续使用,为了解决这个问题,就得使用多线程。

多线程的主要目的就是把各种任务放在子线程里处理,这样的话就可以保证主界面的流畅使用,尤其是需要我们在主界面上打开多个子界面。

因此在学习Pyside/PyQt时,多线程是必定是要学习了解的知识点,我们废话不多说,直接开始!

2.信号与槽

信号与槽是pyside/pyQt中的一个核心概念,学习使用pyside/pyQt进行图形化界面的绘制时必然会使用到,因此我们首先要简略介绍一下,熟练掌握此处知识点的小伙伴可以直接跳过。

在理解信号与槽这个概念时,我们不用把他想的太复杂了,只需要知道:信号的作用就是用于触发槽函数。

我们在使用一个软件例如网易云音乐时,想要播放某首歌,那么点击播放的按钮,歌就开始唱,此时点击这个动作就是一个信号,而歌播放这个过程就是一个函数。

在编写界面时也是如此,我们想要把某个触发的信号与某个函数连接在一起,就常常需要使用connect方法,标准的格式是:

对象.对象的方法信号.connect(槽函数)

如果我们点击一个按钮对象Mybutton时想要触发一个Myfunction函数,那么就写为:

Mybutton.clicked.connect(Myfunction)

如果到此处仍无法理解没有关系,我们继续往下看。

3.结合实例理解

实例1:不使用多线程导致的问题

我们来看第一个实例,该实例想要实现的效果是:点击图形界面上的按钮便开始计时,总共计时10秒,首先来看不使用多线程的情况下发现的问题:

"""
Pyside6# QThread多线程
实例1:体验不使用多线程的结果
作者: 猫猫不吃sakana
"""

from PySide6.QtCore import QThread, Slot, Signal
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QLabel, QTextEdit
import time
from time import sleep


# 主界面,继承QWidget类
class MyGUI(QWidget):
    def __init__(self):  # 重写构造方法
        super().__init__()
        self.setup()  # 创建界面
        self.bind()  # 绑定函数

    def bind(self):
        self.button.clicked.connect(self.timer)  # 将按钮与开启新线程的槽函数通过click信号相连

    def setup(self):
        layout = QVBoxLayout()  # 界面布局为垂直布局
        self.setLayout(layout)  # 设置layout布局
        self.resize(300, 150)  # 重新设置界面大小
        self.label = QLabel("点击按钮开始计时")  # 定义标签控件
        self.button = QPushButton("Click!")   # 定义按钮控件
        self.text_edit = QTextEdit()  # 定义可编辑文本框控件
        layout.addWidget(self.label)  # 放置标签控件
        layout.addWidget(self.button)  # 放置按钮控件
        layout.addWidget(self.text_edit)  # 放置文本框界面

    def timer(self):  # 定义一个10s的计时器
        count = 0
        while count < 10:
            time.sleep(1)
            self.text_edit.setText(f"{count}")  # 将当前数值反映到文本框
            count += 1


if __name__ == '__main__':
    app = QApplication([])  # 定义一个应用
    window = MyGUI()  # 定义一个界面
    window.show()  # 展示界面
    app.exec()  # 启动应用

运行代码后,出现以下界面: 

点击按钮,开始计时:

但是可以发现,事情没有安装我们预想的情况那样进行,界面直接卡住了,变成“未响应”的状态。为什么会产生这样的结果呢?

原因在于在计时的时候使用了sleep方法来计时,但是sleep实际上是通过阻塞线程的方式实现的延时,如果只使用一个线程,那么自然会把仅有的这一个线程卡住。

解决方法自然是通过多线程,创建子线程来计时,而主线程只负责主界面的部分,就不会出现这种一直未响应的情况。多线程的优势就在于此,可以同时处理多个并发任务。

实例2:使用多线程改进上一实例

接下来我们就开始使用多线程对第一个实例进行改进,主线程负责主界面myGUI,而子线程负责计时,其使用自定义的timer函数。

要实现的目标与实例1相同,即点击按钮就开始计时,共计时10s并将数字实时显示在主界面文本框中。

在实例2中,共有两处信号和槽函数的连接:

①点击按钮时,触发创建子线程槽函数,此处点击按钮的信号与子线程槽函数连接

②子线程启动后,开始计时,此处计时的信号与主界面在文本框中实时显示数字的槽函数相连接

实例如下:

"""
Pyside6# QThread多线程
实例2:使用多线程改进实例1
作者: 猫猫不吃sakana
"""

from PySide6.QtCore import QThread, Slot, Signal
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QLabel, QTextEdit
import time
from time import sleep


# 主界面,继承QWidget类
class MyGUI(QWidget):
    def __init__(self):  # 重写构造方法
        super().__init__()
        self.setup()  # 创建界面
        self.bind()  # 绑定函数

    def bind(self):
        self.button.clicked.connect(self.start_new_thread)  # 将按钮与开启新线程的槽函数通过click信号相连

    def setup(self):
        layout = QVBoxLayout()  # 界面布局为垂直布局
        self.setLayout(layout)  # 设置layout布局
        self.resize(300, 150)  # 重新设置界面大小
        self.label = QLabel("点击按钮开始计时")  # 定义标签控件
        self.button = QPushButton("Click!")   # 定义按钮控件
        self.text_edit = QTextEdit()  # 定义可编辑文本框控件
        layout.addWidget(self.label)  # 放置标签控件
        layout.addWidget(self.button)  # 放置按钮控件
        layout.addWidget(self.text_edit)  # 放置文本框界面

    def start_new_thread(self):
        print("启动新线程")
        self.thread1 = myThread()  # 创建子线程thread1
        self.thread1.signal_int.connect(self.update_number)  # 【关键】将信号与槽函数连接
        self.thread1.start()  # 启动子线程thread1

    @Slot(int)  # 定义一个槽函数, 在函数前放一个@Slot()表明其是一个槽函数
    def update_number(self, count):
        self.text_edit.setText(f"{count}")  # 将计时器传来的信号展示在文本框中


# 子线程类,继承QThread类
class myThread(QThread):
    signal_int = Signal(int)  # 定义信号

    def __init__(self):
        super().__init__()

    def run(self):
        self.timer()  # 重写run()方法,启动线程时自动调用并运行timer函数,开始计时

    def timer(self):  # 定义一个10s的计时器
        print("开始计时")
        count = 0
        while count < 10:
            time.sleep(1)
            count += 1
            self.signal_int.emit(count)  # 【关键】每秒发送一次信号,将当前计数数字传至主界面显示
        print("结束计时")


# 运行应用界面程序
if __name__ == '__main__':
    app = QApplication([])  # 定义一个应用
    window = MyGUI()  # 定义一个界面
    window.show()  # 展示界面
    app.exec()  # 启动应用

实现的效果如下:

可以看到,此时计时正常进行,主界面无响应的情况也不再发生了,这就是多线程的作用。虽然此处只使用了time阻塞线程的这一特例,但是在其他很多情况下都可以使用,尤其是项目较为复杂时,需要同时进行多并发的任务,多线程就必不可少了。

感谢各位支持,之后还会继续分享更多有用的知识!想要了解更多可以关注我或关注本专栏。

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PySide2是一个用于创建跨平台图形用户界面Python库。在使用PySide2进行多线程编程时,可以通过创建新线程、子线程发射信号到主界面来实现多线程操作。\[1\] 在示例代码中,首先导入了必要的模块,包括QApplication、QTextBrowser、QUiLoader、Thread和Signal等。然后定义了一个自定义的信号源对象类型MySignals,该对象继承自QObject,并定义了两种信号类型。接着实例化了一个MySignals对象global_ms,并在Stats类的构造函数中将自定义信号的处理函数与信号连接起来。\[2\] 在task1和task2函数中,通过创建新线程并在其中调用threadFunc函数来实现多线程操作。在threadFunc函数中,通过emit方法触发执行主线程中的处理函数,从而实现子线程发射信号到主界面的功能。\[2\] 除了使用Thread模块,还可以使用QThread类来实现多线程操作。可以简单修改线程类的定义,重写run方法,并在主线程中创建QThread对象来启动线程。需要注意的是,线程不能被垃圾回收,可以通过增加self引用来防止线程对象被垃圾回收。\[3\] 总结来说,PySide2可以通过创建新线程、子线程发射信号到主界面来实现多线程操作。可以使用Thread模块或QThread类来创建线程,并通过信号与槽机制来实现线程间的通信。 #### 引用[.reference_title] - *1* [PySide2多线程问题示例:创建新线程、子线程发射信号到主界面](https://blog.csdn.net/xhzc7/article/details/116702475)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [PYQT5|Pyside2 后台线程 与 信号方式多线程防止界面卡死](https://blog.csdn.net/zh6526157/article/details/121797339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [PySide2----多线程](https://blog.csdn.net/weixin_45228198/article/details/128439449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值