pyqt5 QThread 子线程 出错记录和处理总结

文章介绍了在PyQt5应用中如何使用子线程避免主界面卡顿,强调了子线程生命周期管理和安全退出的重要性,提供了避免Destroyedwhilethreadisstillrunning错误的方法,并提到了信号槽机制在子线程与主线程间通信的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了防止主界面卡顿,不得不学习子线程。遇到了一些问题并解决,做下总结,留作以后查阅。也希望能给同级别的初学者提供参考,少走点弯路。

先写代码,子线程的代码基本上很固定,我经过学习总结如下。

from PyQt5 import QtCore
import time
import traceback


class Thread(QtCore.QThread):
    # 完成信号
    signal_finished = QtCore.pyqtSignal()

    # 重写构造函数,设置父类参数,实例化时将父类设置为主界面ui,
    #    可保证线程的生命周期。
    # 此处可用桥接的方式传入主ui,但可能造成参数多次多级别传递,
    #    不方便维护。我推荐将主ui做成单例模式,方便其他模块调用。
    def __init__(self, parent=None):
        super().__init__(parent=parent)

    # 非正常结束时触发,防止错误: Destroyed while thread is still running
    #    (下面统一称为[destroyed错误...])
    def __del__(self):
        self.wait()

    def run(self):
        # 主线程无法获取子线程的错误信息,用try和traceback返回错误信息。
        try:
            print('线程开始')

            # 这里写代码,代码也可写成函数在此调用,暂时用time.sleep代替
            time.sleep(5)

            # 运行完代码,用quit函数退出线程,这句最好有,
            #    配合__del__(self)防止[destroyed错误...]
            self.quit()

            # 下面这句不要尝试放在run里面,会弹出提示Thread tried to wait on itself,
            # 虽不会出错,但应该没用。
            # self.wait()  

            self.signal_finished.emit()  # 经测试,信号可以放在quit后面,不影响信号发出
            
            # 因为quit之后还需要1丢丢时间,立即结束可能出现[destroyed错误...],
            # 因为有了__del__(self)所以不考虑这个延迟了,可以留着做双保险。
            # time.sleep(1)
        except:
            print(traceback.format_exc())


# 运行线程, 将QThread的父类设为 main_window.ui ,保证生命周期.
# 建议将main_window做成单例,单独模块并直接实例化,
#    将uic.loadUi("main_window_ui.ui")封装在函数中,APP后调用(这里不细说了)
def run_thread():
    # 实例化需要主界面配合,主界面不在这说了,说了就太多了。
    th = Thread(main_window.ui)
    th.signal_finished.connect(finished_thread)
    th.start()
    # th.wait()  # 这句不能有,会卡主界面,我搞子线程就是为了不卡主界面


# 线程信号signal_finished的槽函数,可以用来放弹窗(弹窗等控件不能放在子线程中),
#    也可以激活别的线程。
def finished_thread():
    pass

如果用的pycharm,推荐:运行>编辑配置>模拟终端>模拟输出控制台的终端:打钩。这样会弹出错误提示。

错误:Destroyed while thread is still running

这个错误最常见,意思是子线程“非正常终结”,会导致程序崩溃。

下面是我的理解,供参考:

原因1:生命周期不足

子线程的生命周期依赖实例化它的对象,或者它的父类。

注意“生命周期”这个词,很关键。生命周期就是指在内存中从开始到结束的时间,子线程的生命周期就是它运行所需要的时间;函数的生命周期就是函数从调用到其运行完毕;class就是从实例化到被回收,主界面ui就是从双击运行程序到程序结束。

举例: th = MyThread(),这个写法放在函数中就很容易报这个错误,因为函数的生命周期一般是比子线程短的多。

修改1:绑定类,写成 self.th = MyThread(),这样的话,只要所在的类没有被回收,就能保证子线程一直运行。

修改2(推荐):将主界面ui作为父类传入,写成 th = MyThread(main_window.ui),这样的话,只要程序不退出,就能保证子线程一直运行。

原因2:未安全退出(子线程完成run后不会立刻停止)

为了保证子线程安全退出,推荐quit函数配合__del__函数

1.在run函数末尾添加quit函数:self.quit()

2.子线程类添加下面的代码

    def __del__(self):
        self.wait()

错误:闪退

这个错误可能是上面的错误导致,也可能是在子线程中运行了控件,注意“控件”2个字,推荐子线程所有的数据交流全部用信号,也就是说子线程只操作信号,不要操作任何控件。

信号可以传输任意想要的数据,注意在创建信号时写明参数个数和数据类型,槽函数参数与其一一对应:

signal_msg1 = QtCore.pyqtSignal(str)
signal_msg2 = QtCore.pyqtSignal(str, bool)
signal_msg3 = QtCore.pyqtSignal(list, dict)

感谢:

我查阅了很多前辈的资料,因为太多,所以在此不罗列了,感谢前辈们的分享!!

PyQt5是一个Python绑定库,它允许开发者使用Qt框架创建图形用户界面应用。在PyQt5中,为了在主线程子线程之间通信,通常会使用`QObject`类以及其提供的`emit()`函数来发送信号(signal),而`connect()`函数用于连接信号槽(slot)。 当主线程需要向子线程发送信息时,通常会在主线程的`QObject`上发出一个信号,这个信号可以携带数据。例如,你可以创建一个名为`sendingSignal`的自定义信号: ```python from PyQt5.QtCore import pyqtSignal class MyThread(QObject): sendingSignal = pyqtSignal(str, int) # 创建一个字符串整数类型的信号 def process_data(self, data_string, data_integer): # 子线程处理逻辑... result = do_something(data_string, data_integer) self.sendingSignal.emit(result, some_other_data) # 发送信号到主线程 ``` 然后,在主线程中,你需要创建一个`MyThread`实例,并通过`connect()`将子线程的信号与主线程中的槽函数关联起来: ```python def handle_data_from_thread(sender, data, other_data): print(f"Received data from thread: {data}, Other Data: {other_data}") thread = MyThread() thread.moveToThread(qt_core.QThread()) # 将线程移到独立的线程中 # 连接信号槽 thread.sendingSignal.connect(handle_data_from_thread) # 主线程启动子线程并发送信号 thread.start() thread.process_data("some string", 42) ``` 这样,每当子线程的`sendingSignal`被触发,主线程的`handle_data_from_thread`就会被执行,接收到来自子线程的数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值