教你pyqt正确使用异步,避免界面卡死问题



前言

使用pyqt画界面,当一个点击事件过于复杂,或者出现阻塞时,这个时候往往就会导致界面卡死,而我们只是单纯的起一个线程,并不能解决问题,反而直接使整个程序崩掉,而这个时候,我们想要解决这个问题,只有使用pyqt自带的线程库QThread


一、那么QThread具体怎么使用呢?

网上有很多例子,大多是创建一个类,继承QThread类,然后引入一个槽函数,然后改写run方法,将会发生阻塞的代码放在run函数里面,然后通过激活槽函数,传递一个参数,从而实现异步功能

class MyThread(QThread):
    countChanged = pyqtSignal(int)
 
    def __init__(self, parent=None):
        super(MyThread, self).__init__(parent)
        self.count = 0
 
    def resetCount(self):
        self.count = 0
 
    def run(self):
        while True:
            self.msleep(100)
            self.count += 1
            self.countChanged.emit(self.count)

参考网址如下:
https://blog.csdn.net/seniorwizard/article/details/110824177

二、个人认为,其实可以更简单,直接继承QThread类,没必要绕来绕去

import time

import sys
from PyQt5 import QtWidgets

from PyQt5.QtCore import QThread


# 装饰器,用于测量阻塞计时
def test_time(func1):
    def train(self):
        start_time = time.time()
        res = func1(self)
        end_time = time.time()
        print(end_time - start_time)
        # logger.info(f'the ocr parse time is {end_time-start_time} s')
        return res

    return train


class pictureOCR(QThread):
    """
    对图片进行ocr识别,,功能服务,可单独放一个文件
    """

    def __init__(self, *args, **kwargs):
        super(pictureOCR, self).__init__()

    @test_time
    def run(self):
        while True:
            time.sleep(2)  # 制造阻塞
            print('任务执行中')


class MainWindow(QtWidgets.QMainWindow):
    """pyqt主界面"""
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        # self.setupUi(self)
        self.resize(500, 300)

        self.p = pictureOCR()

        self.pushButton = QtWidgets.QPushButton(self)
        self.pushButton.setText('开始异步任务')
        self.pushButton.clicked.connect(self.click_event)

    def click_event(self):
        self.p.start()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ui = MainWindow()
    ui.show()
    sys.exit(app.exec_())

将功能和界面分开,是不是更加清晰明了,而且界面也不会出现未响应的情况,如果你有更好的办法,欢迎一起交流

补充

上述方法,确实能够解决异步卡顿问题,但同时引出了一个新问题,那就是在直接刷新主界面控件时,比如表格控件,并不会立即刷新,需要点击一下才可以刷新,所以还是需要在该方法中引入槽函数
改造后代码如下:

import time

import sys
from PyQt5 import QtWidgets, QtCore

from PyQt5.QtCore import QThread


# 装饰器,用于测量阻塞计时
def test_time(func1):
    def train(self):
        start_time = time.time()
        res = func1(self)
        end_time = time.time()
        print(end_time - start_time)
        # logger.info(f'the ocr parse time is {end_time-start_time} s')
        return res

    return train


class pictureOCR(QThread):
    """
    对图片进行ocr识别,,功能服务,可单独放一个文件
    """
    signal = QtCore.pyqtSignal(int)

    def __init__(self, *args, **kwargs):
        super(pictureOCR, self).__init__()
        self.main_win = kwargs.get('main_win')
        self.signal.connect(self.refresh)

    @test_time
    def run(self):
        m = 0
        while True:
            time.sleep(2)  # 制造阻塞
            m += 1
            self.signal.emit(m)
            print('任务执行中')

    def refresh(self, m):
        self.main_win.line_edit.setText(str(m))


class MainWindow(QtWidgets.QMainWindow):
    """pyqt主界面"""


    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        # self.setupUi(self)
        self.resize(500, 300)

        self.p = pictureOCR(main_win=self)  # 把主函数对象传给服务,方便服务操作控件

        self.pushButton = QtWidgets.QPushButton(self)
        self.pushButton.setText('开始异步任务')
        self.pushButton.clicked.connect(self.click_event)

        self.line_edit = QtWidgets.QLineEdit(self)
        self.line_edit.move(200,0)

    def click_event(self):
        self.p.start()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ui = MainWindow()
    ui.show()
    sys.exit(app.exec_())
PyQt中,如果在主界面线程中执行耗时操作,会导致界面卡死问题。这是因为PyQt应用程序是基于事件驱动的,主线程负责处理GUI事件。当有耗时的操作任务时,GUI事件会被阻塞,导致应用程序处于假死状态,无法与应用程序进行交互。为了解决这个问题,可以采用多线程的方式。一种方式是继承自QThread类,将耗时的操作放在子线程中进行处理,然后通过signal-slot机制将子线程的数据反馈到主界面线程中。需要注意的是,在子线程中不能操作界面,只能处理数据。这样可以将UI的操作与耗时数据的处理进行分开处理,避免界面卡死问题。另一种方式是使用RunThread类继承自QObject,而非继承自QThread。这种方式将数据的处理与线程的创建与启动分开进行处理,适用于某些场景下比较方便。虽然这种方式比较复杂,但在特定情况下可以更灵活地使用多线程。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [PyQt - 使用多线程避免界面卡顿](https://blog.csdn.net/bailang_zhizun/article/details/109240670)[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^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [解决PySide6/PyQT界面卡死问题(PySide6/PyQT多线程](https://blog.csdn.net/weixin_45081575/article/details/130210522)[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^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方寸之间 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值