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

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

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

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)

感谢:

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

你可能感兴趣的:(Python,python,pyqt,ui)