PyQtの科学はスレッドを使って時間のかかるタスクとスレッドの通信方法を処理します


目次
前言
PyQtスレッド科学の使い方
非科学的な使い方の例
科学的用法
スレッドクラス
スレッドつうしん
スレッドクラスのメインインタフェースでのインスタンス化と使用
スレッドを開く
補足(信号方式でスレッド双方向通信を実現):
スレッドクラス
スレッドのインスタンス化とオープンスレッドのバックグラウンド
送信信号、スレッド処理データ
締めくくり
参考記事
 
前言
本文は主にPyQtがマルチスレッドモジュールQThreadを用いてPyQtインタフェースプログラムが時間のかかる操作を実行する時、プログラムカートンの現れた応答がないこととインタフェース出力がリアルタイムで表示できない問題を解決することを説明する.時々、私たちが書いたプログラムインタフェースが応答しないのは、長時間実行する必要があるコードをプライマリスレッドに配置し、イベントループをブロックしているからです.
QtCore.QThreadはスレッドを管理するクラスで、そのコンストラクション関数を使用すると、新しいスレッドが作成されます.ここで強調したいのは、QThreadはスレッドマネージャであり、ビジネスロジックをこのクラスに入れないでください.Qtの著者は、QThreadクラスを継承してビジネスロジックを実現する方法を何度も批判しています.しかし、私はネット上で多くの資料やブログを見て、大部分はQThreadを継承してビジネスロジックを実現しています...もちろん科学で使われているチュートリアルもたくさんありますが、比較的少ないです.私は今日私の科学をPyQtマルチスレッドを使って、心の中で分かち合って、みんなの参考に供します.
PyQtスレッド科学の使い方
非科学的な使い方の例
非科学的な使い方を紹介していますが、QThreadを直接継承して業務を書いています.では、ネット上の多くのチュートリアルはこのようなものです....
class Thread(QThread):
    def __init__(self):
        super(Thread,self).__init__()
    def run(self):
        #      

#        
thread = Thread()
thread.start()

科学的用法
Qtの著者は、QThreadクラスを継承してビジネスロジックを実現する方法を何度も批判していますが、PyQtマルチスレッドを科学的に使用するにはどうすればいいのでしょうか.答えは次のとおりです.
自分でスレッドクラスを定義し、このクラスはQObjectを継承し、そこでスレッドに関するビジネスロジックを実現し、同時にメインインタフェースでこのスレッドクラスをインスタンス化し、moveToThreadメソッドでQThread管理に移動します.
# fixme PyQt      
self.thread = QThread()
#        workThread        ,      
self.work_thread = workThread()
#         moveToThread  QThread  
self.work_thread.moveToThread(self.thread)

スレッドクラス
まず自分のスレッドクラスを書いて、業務機能を実現します.私のところは画像処理に時間がかかるので、元のコードを直接貼って、少し修正しました.スレッド間の通信や、リソース競合があるかどうかなどに注意してください. QtCore.SignalとQtCore.pyqtSignalは同じですが、ここではオープンソースのlabelmeと書いています.いずれも信号定義の方式です.
class workThread(QObject):
    #        
    to_show_img_signal = QtCore.Signal(QtGui.QImage)

    def __init__(self):
        super(workThread, self).__init__()
       
    def work(self):
       
        global imageData
        global mask_list
        """
            
              
        """
        #        
        self.to_show_img_signal.emit(qimage)

   

スレッドつうしん
スレッド通信方式:グローバル変数、メッセージング(PyQt信号スロット機構)
ここではグローバル変数を使用しています.MianwindowクラスとworkThreadクラスの外に2つのグローバル変数を定義しています.2つのクラスで変更量を使用するにはglobalを加えなければ、グローバル効果を実現できません.
なぜグローバル変数を使用し、スレッド通信の方法として、なぜ信号スロットを使用しないのか疑問に思うかもしれません.私も最初は信号スロットを使いたいと思っていましたが、そうしましたが、間違えてしまいます.ここではスレッドを使用すると同時にメインインタフェースから画像データをサブスレッドに送信する必要があるので、サブスレッド処理が完了したら、メインインタフェース表示に送信します.すなわち,スレッド双方向通信である.
信号スロットメカニズム:サブスレッドがホームページに信号やデータを送信するのは確かに便利です.しかし、メインインタフェースがサブスレッドにデータを送信すると問題が発生します.信号スロットプログラムを使用すると、信号を取得できないconnectメソッドというエラーが発生します.
'PyQt5.QtCore.pyqtSignal' object has no attribute 'connect

上の新聞の間違いは、私も以前出会ったことがあります.解決策もありますが、今回の問題はこの新聞の間違いとは関係ありません.
信号の正確な定義と使用、および上記のエラー解決策:https://memory-qianxiao.blog.csdn.net/article/details/105754667
したがって,信号スロットはサブスレッドがメインインタフェースに信号を送信するために用いられると推測し,メインインタフェースがサブスレッドにデータを送信するには,他の方法で実現する必要がある. 
スレッドクラスのメインインタフェースでのインスタンス化と使用
ここに貼られたコードは私のコードの中のスレッドが簡略化されたもので、みんなの理解を便利にするために、同時に注釈を書いて、比較の核心の何行のコードはinitの中の何行です.これは科学的にPyQtスレッドを使ったテンプレートです.特にスレッドが完了すると、スレッドを閉じる必要があり、スレッドは自分で閉じることはありません.
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        # fixme PyQt      
        self.thread = QThread()
        #       
        self.work_thread = workThread()
        #moveToThread          Thread  
        self.work_thread.moveToThread(self.thread)
        #         ,         
        self.thread.started.connect(self.work_thread.work)
        #            
        self.work_thread.to_show_img_signal.connect(self.show_img_in_labelme)
        #           
        self.thread.finished.connect(self.threadStop)

    def threadStart(self):
        #     
        self.thread.start()

    def threadStop(self):
        #     
        self.thread.quit()

    #          
    def show_img_in_labelme(self, qimage):
        self.onNewBrightnessContrast(qimage)

スレッドを開く
ここで特に注意しなければならないのは、スレッドのオープンはメインインタフェースの中にあり、処理に時間がかかる操作が必要な場合は、自発的にスレッドを開き、処理に時間がかかるタスク、処理が完了し、検出スレッドが実行された場合は、オフにする必要があります.オン方法は既に定義されています.したがって、使用する場所で関数を呼び出すだけです.
def eraser_or_brush(self, coordinate):
    #    
    global mask_list  
    global imageData
    #      
    """     """
      
    """    (     )"""

    #        
    self.thread.start()

    #           
    self.actions.undo.setEnabled(self.isHasMaskImage())

補足(信号方式でスレッド双方向通信を実現):
補充期間:20201年1月5日
以上の方式はグローバル変数に基づいて,メインスレッドとサブスレッドの通信を実現し,サブスレッド処理が完了してメインスレッドに信号を送信する.ここでもう一つの書き方の考え方を補足します:メインスレッドが始まると(init初期化)1つのスレッドがバックグラウンドに掛けられ、必要なときはデータと信号を送信し、スレッドに処理させ、処理が終わったらメインスレッドに信号を送信します.
スレッドクラス
上記のスレッドクラスとは異なり,ここではスレッド処理方法に2つのパラメータ1つの信号が追加されている.
from qtpy.QtCore import QObject, QThread
from qtpy import QtCore, QtGui
import cv2
import numpy as np
from . import utils
import time

class ImageProcessingThread(QObject):
    #          
    to_show_img_signal = QtCore.Signal(QtGui.QImage)
    #        
    to_start_image_process_thread_signal = QtCore.Signal(bytes, list)

    def __init__(self):
        super(ImageProcessingThread, self).__init__()
     
    #          
    def work(self, imageData, mask_list):
         
        """        """
        
        #        
        self.to_show_img_signal.emit(qimage)

スレッドのインスタンス化とオープンスレッドのバックグラウンド
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        # fixme PyQt      
        self.thread = QThread()
        #         
        self.image_process_thread = ImageProcessingThread()
        self.image_process_thread.moveToThread(self.thread)
        #      
        self.image_process_thread.to_start_image_process_thread_signal.connect(self.image_process_thread.work)
        self.image_process_thread.to_show_img_signal.connect(self.show_img_in_labelme)
        #            
        self.thread.start()

送信信号、スレッド処理データ
def eraser_or_brush(self, coordinate):
    #       
    """     """
    """    (     )"""
    
    #        ,       
    self.image_process_thread.to_start_image_process_thread_signal.emit(self.imageData, self.mask_list)
    #           
    self.actions.undo.setEnabled(self.isHasMaskImage())

 
締めくくり
私のこの文章があなたに役に立つことを望んで、三連を望んで、感謝に堪えません~
マルチスレッド、マルチプロセスというのは実は難しいです.コードは短いですが、ある複雑な機能を具体的に実現するには、本当に容易ではありません.多くの時間をかけて穴を掘って、他の人の参考資料を見に行きます.今、ネット上の資料が多すぎて、多くのブロガーは直接転載したり、他の人の文章をそのまま発表したりしています.もっと時間をかけて資料を探しに行きましょう.本当につらいです.
私のこの文章は主に私の心得と簡略化のコードを貼って、みんなに勉強させて、1つのテンプレートに相当します.皆さんは穴を掘る過程が少なくなって、マルチスレッドに対する理解が足りないかもしれませんが、私のいくつかのやり方を理解できませんが、私は役に立つと思います参考文章を後ろに貼ってあげます.皆さんの参考にして、理解してください.
参考記事
フォントをクリックすると文章に入ります
  • PyQtスレッドを用いた正しい姿勢
  • pyqt 5のマルチスレッド(QThread)が遭遇するピット(一)
  • PyQt 5開発学習(3)--moveToThread非同期リフレッシュUI
  • を使用
  • pyqtマルチスレッドmoveToThreadの使用
  •