教你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_())
当你在使用PyQt5和requests库时,如果在主线程中进行网络请求,会导致界面卡死,因为网络请求是阻塞的。为了避免这种情况,你可以使用多线程或异步编程来处理网络请求。 下面是一个使用多线程的示例: ```python import sys import requests from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel from PyQt5.QtCore import QThread, pyqtSignal class RequestThread(QThread): finished = pyqtSignal(str) def __init__(self, url): super().__init__() self.url = url def run(self): response = requests.get(self.url) self.finished.emit(response.text) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.label = QLabel(self) self.label.setGeometry(50, 50, 200, 30) self.thread = RequestThread('https://www.baidu.com') self.thread.finished.connect(self.on_finished) self.thread.start() def on_finished(self, text): self.label.setText(text) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 在这个示例中,我们创建了一个名为RequestThread的子线程来处理网络请求。该线程接收一个url参数,并在运行时使用requests库进行网络请求。当请求完成后,线程发出一个finished信号,并将响应文本作为参数传递给该信号。在主窗口中,我们创建了一个QLabel来显示响应文本,并将其位置设置为(50,50)。然后,我们创建了一个RequestThread实例并将其启动。当线程发出finished信号时,我们将其连接到on_finished方法,该方法将响应文本设置为标签的文本。 使用多线程可以防止网络请求阻塞界面,但它并不是最佳的解决方案。使用异步编程可以更好地处理网络请求,因为它不会阻塞任何线程。如果您对此感兴趣,可以尝试使用asyncio和aiohttp库来实现异步网络请求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

方寸之间 

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

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

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

打赏作者

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

抵扣说明:

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

余额充值