pyqt5 的多線程(QThread)遇到的坑(一)

前言

不管網上搜索QThread用法還是網上授課老師的教程,對QThread的介紹都是最簡單最基本的功能,如下:
創建線程類:

class Thread(QThread):
 	sinOut = pyqtSignal(str)   # 定義信號便於傳遞數據
    def __init __(self):
        super(Thread,self).__ init __()
    def run(self):
        #線程相關的代碼
        ...
        # 發射信號
        self.sinOut.emit(file_str)

創建一個新的線程

thread = Thread()
thread.start()

舉例的代碼也是上面這幾行代碼的反覆使用,比如弄個定時器顯示個時間什麼的。但是,再我們平時開發中,並不會這麼簡單,那麼坑就來了。

第一個坑:給線程類傳參

在UI中調用線程一般是因爲一些比較耗時的工作會導致界面假死,但是往往這些耗時工作需要一些現有數據,那麼就需要給線程傳參,上面線程內定義的信號傳遞數據是把線程內的數據傳遞出去,但是我這裏是想說把數據傳遞進來,讓線程進行一些耗時運算,運算完了再把運算結果數據傳遞出去。這裏只要注意,傳參只能在線程類的 init(self,agrs)中傳參,run(self)是不能傳參的!傳參的類型很多,可以是數據也可以是對象。

第二個坑:傳遞一個類的實例

比如我定義了一個類進行數據運算,運算耗時很長,我需要把數據運算類的工作使用多線程處理, 數據運算類:

class ModifyData(object):
    def __init__(self, data):
        self.data = data  # 接收原始數據
    def analyze(self):
    	...  # 數據處理過程省略
        return self.data   # 返回數據處理結果

那麼在UI窗口中就要使用多線程了,好吧,先定義一個線程類,上面第一個坑提到了傳參的問題,我們直接在線程類初始化時加入參數 argument_,這個參數是用來接受上面定義的數據處理類ModifyData():

class MoreThreadUse(QtCore.QThread):
    update_date = QtCore.pyqtSignal(pandas.DataFrame)   # 定義信號
    def __init__(self, argument_):
        super().__init__()
        self.fuc = argument_
    def run(self):
        a = self.fuc.analyze()
        self.update_date.emit(a)
        time.sleep(10)
        print("")

這裏還需要一個UI的工作界面類,我們是在這個UI中使用線程進行數據分析的,但是在這個Ui類中,我們如何把前面的數據分析類交給多線程來處理呢?直接把類名ModifyData傳遞過去嗎?對不起!當你用start()啓動線程的時候會直接崩潰!有兩種傳遞方式:第一、先把數據處理類ModifyData實例化,再把實例化對象傳參過去!第二, 把原始數據傳參給自定義多線程類,在多線程類的初始化中方法中實例化數據處理類ModifyData;下面是UI的工作界面類,使用第一種方法開啓多線程:

class ThreadUI(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("UI")
        self.resize(500, 300)
        self.input_line = QtWidgets.QLineEdit(self)
        self.input_line.resize(400, 200)
    def abc(self):
        path = "E:\\*****\\***.xls"
        data = pandas.read_excel(path)
        ann = ModifyData(data)
        self.thread_1 = MoreThreadUse(ann)
        self.thread_1.update_date.connect(self.handle_display)
        self.thread_1.start()  
    def handle_display(self, data):
        self.input_line.setText(data)
第三個坑:信號傳參

大家都在說,在線程類種定義一個信號,方便與傳遞數據,雖然可以傳遞很多常見數據,但是並非所有數據都可以傳遞,上面的ui類種可以看到,要傳遞的數據是pandas打開的一個excel表格,其實這個表格有幾萬條數據,上面的往線程內傳參解決了,那使用線程數據處理完了,如何把數據傳遞出來呢?用信號嗎?不可以,直接崩潰了!因爲數據格式是pandas.DataFrame ,信號不能傳遞這類數據,肯定還有其他類型不能傳遞,這裏不列舉了,只要知道這回事就可以了,那麼怎麼處理呢?信號是肯定不能用了,我暫時沒想到更好的辦法,我這裏是使用了寫入文件處理的,因爲我的數據分析非常耗時,所有分析過程種可以做很多其他事情,直接把分析結果寫入一個文件就ok了。正確的代碼邏輯如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas
import time
class MoreThreadUse(QtCore.QThread):
    # 多線程,傳遞參數爲一個類的實例,不用信號傳遞數據,直接把數據寫入文件
    def __init__(self, argument_):
        super().__init__()
        self.fuc = argument_
    def run(self):
        self.fuc = ModifyData(self.fuc)
        data = self.fuc.analyze()
        data.to_excel("D:\\****.xls")  # 直接把數據處理結果寫入文件,使用pandas.to_xxx寫文件很方便
        time.sleep(10)
        print(a)
class ThreadUI(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("UI")
        self.resize(500, 300)
        self.input_line = QtWidgets.QLineEdit(self)
        self.input_line.resize(400, 200)
    def abc(self):
        path = "E:\\*****\\***.xls"
        data = pandas.read_excel(path)
        ann = ModifyData(data)
        self.thread_1 = MoreThreadUse(data)
        self.thread_1.start()  
    def handle_display(self, data):
        self.input_line.setText(data)
class ModifyData(object):
    def __init__(self, data):
        self.data = data
    def analyze(self):
		...  # 數據處理過程省略
    	return self.data   # 返回數據處理結果
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main = ThreadUI()
    main.abc()
    main.show()
    sys.exit(app.exec_())
最後一個坑:創建線程類的變量

上面的例子種可以看到,線程類實例化時變量是 self.thread_1 一定要加上self,如果不加,thread_1就是一個局部變量,當其所在方法運行結束的時候,它的生命週期也都結束了,但是這個線程裏的程序很有可能還沒有運行完!可能會報錯:QThread :Destroyed while thread is still running!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章