pyqt 5のマルチスレッド(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)はパラメータを伝達できません.パラメータのタイプは多く、データでもオブジェクトでも構いません.
2番目のピット:クラスのインスタンスを渡す
例えば、私は1つのクラスを定義してデータ演算を行い、演算に時間がかかります.私はデータ演算クラスの仕事をマルチスレッド処理を使用する必要があります.データ演算クラス:
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)

3番目のピット:シグナルパラメータ
オンラインクラス種は信号を定義し、データを伝達しやすく、多くの一般的なデータを伝達することができるが、すべてのデータが伝達できるわけではない.上のuiクラス種は、pandasが開いたexcelテーブルであることを見ることができるが、実はこのテーブルには数万本のデータがあり、上のスレッド内へのパラメータ伝達が解決されている.では、スレッドデータを使って処理が完了したら、どのようにデータを渡しますか?信号を使いますか?いいえ、直接崩れました!データフォーマットはpandasだからDataFrame、信号はこのようなデータを伝達することができなくて、きっとその他のタイプが伝達することができなくて、ここで列挙しないで、この事を知っていればいいので、それではどのように処理しますか?信号はきっと使えません.私はしばらくもっと良い方法を考えていませんでした.私はここで書き込みファイルを使って処理しました.私のデータ分析には時間がかかりますから、すべての分析過程で他のことをすることができます.直接分析結果を1つのファイルに書き込めば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!