0

これが私がQThreadに基づいて書いた私の素敵なスレッドです。イベントキューがあることに気付くでしょう。4秒後、イベントが発生し、でいくつかの作業が行われdoWorkます。doWorkすべての印刷の間にスリープし、他のスレッドに実行の機会を与える必要があります。doWorkすべての印刷とスリープの実行が十分に長く、別のスレッドが実際に実行するのに時間がかかると言えば十分です。

from PySide.QtCore import *
from PySide.QtGui import *

class DoStuffPeriodically(QThread):
    def __init__(self):
        super(DoStuffPeriodically, self).__init__()

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,100):
            print "work %i" % i
            QThread.msleep(10)
        print "Done work"
        return

    def run(self):
        """ Setup "pullFiles" to be called once a second"""
        self.timer= QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.doWork)
        self.timer.start(4000)
        self.exec_()

これが私のスレッドを制御するために使用しているトップレベルのQTウィジェットです。基本的には、スレッドを開始/停止するプッシュボタンだけです。

class Widg(QWidget):
    def __init__(self):
        super(Widg, self).__init__()
        self.thread = DoStuffPeriodically()
        self.startStopButton = QPushButton()
        hBoxLayout = QHBoxLayout()
        hBoxLayout.addWidget(self.startStopButton)
        self.startStopButton.pressed.connect(self.startStopThread)
        self.setLayout(hBoxLayout)
        self.threadRunning = False

    def startStopThread(self):
        if self.threadRunning:
            print "Stopping..."
            self.thread.exit(0)
            self.threadRunning = False
            print "Stopped"
        else:
            print "Starting..."
            self.thread.start()
            self.threadRunning = True
            print "Started"


if __name__ == "__main__":
    from sys import argv
    qApp = QApplication(argv)    
    widg = Widg()
    widg.show()
    qApp.exec_()

startStopButtonをクリックすると、スレッドが印刷を開始するのを確認できます。

起動...
開始しました...
仕事を始める
仕事0
仕事1
..。
仕事99
完了しました

しかし、私がやりたいのは、スレッドが動作している間、スレッドを停止できるようにすることです。私はの線に沿って何かを期待しています

起動...
開始しました...
仕事を始める
仕事0
仕事1
..。
仕事N
停止しています...
仕事99
完了しました
停止...

代わりに、ワーカースレッドがメインスレッドの実行を妨げているように見えますか?そして、startStopButtonをクリックする前に、作業が完了するのを待つ必要があります。

起動...
開始しました...
仕事を始める
仕事0
仕事1
..。
仕事99
完了しました
停止しています...
停止...

実行時間は関係ありませんdoWork。私はそれを10000回ループするように上げました。メインスレッドに時間を戻すことはないようで、ウィジェットは応答しません。実際のスレッドが実際に機能するのを妨げるようなことをしていますか?

(私はpython2.7とpyside1.10を使用しています。)

アップデート

runスレッドに基づいていない直接作業を行うように変更すると、QTimer正しく機能しているように見えます。つまり、次のように変更runします。

def run(self):
    self.doWork()
    return

イベントキューを使用して実行したいので、これでは問題は解決しません。QTimerしたがって、これは、信号が間違ったスレッドに関連付けられている、ある種の信号/スロットの問題であると思われます。

作業が完了するまで、私はそれを経験していないexitか、ブロックしていないことに注意してください。quitスレッドがまったく機能しないのを経験しているだけです。つまり、メインウィンドウがブロックされており、ボタンをクリックしてスレッドを終了することすらできません。

4

2 に答える 2

3

問題は、QThreadメソッドが機能していることです。のスレッド アフィニティは、QThread常にQThreadを作成したスレッドです。したがって、シグナルはQThreadの所有スレッド (doWorkこの場合はメイン スレッド) に実行を指示します。したがってdoWork、この QThread で定義されていても、作業はメイン スレッドによって行われます。私は一種の心のねじれを知っています。説明するために、ドキュメントを引用することから始めましょう

QThread オブジェクトは別のスレッド、つまりそれが作成されたスレッドに存在しています。

したがって、この信号/スロット接続がセットアップされると

 self.timer.timeout.connect(self.doWork)

それは、デフォルトでAutoConnectionです:

(デフォルト) シグナルが受信オブジェクトとは異なるスレッドから送信された場合、シグナルはキューに入れられ、Qt::QueuedConnection として動作します。それ以外の場合、スロットは直接呼び出され、Qt::DirectConnection として動作します。接続のタイプは、信号が発信されたときに決定されます。

メソッドで QTimer が作成されたため、信号のソースは私の QThread ですrunが、宛先はメイン スレッドです。メインスレッドのイベントキューにキューイングされています! 解決策は、現在のスレッドのアフィニティを持つ 2 番目のワーカー QObject を作成することです。

class Worker(QObject):
    def __init__(self, parent):
        super(Worker, self).__init__(parent=parent)

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,1000):
            print "work %i" % i
            QThread.msleep(100)
        print "Done work"
        return

次に、実行は次のようになります。

def run(self):
    """ Setup "pullFiles" to be called once a second"""
    print "Running..."       
    self.worker = Worker(parent=None) #affinity = this thread
    self.timer= QTimer() #affinity = this thread
    print self.timer.thread()
    self.timer.setSingleShot(True)
    self.timer.timeout.connect(self.worker.doWork)
    self.timer.start(4000)
    self.exec_()
    print "Exec_ done"

そして、これは機能します。シグナルの送信元と送信先はすべて 1 つのスレッドにあり、メイン スレッドに戻ることはありません。出来上がり!

于 2012-08-15T16:05:19.940 に答える
0

QThread::exit()ドキュメントから:

Tells the thread's event loop to exit with a return code.

あなたdoWorkはイベントループ内の単一のイベントです。イベント ループがイベントを呼び出したので、イベントが終了するのを待ちます。は、イベント ループのキューに入れられ、終了exitを待機するもう 1 つのイベントです。doWorkこれmsleepにより、GUI の応答性が向上します (再描画してボタン ハンドラーを実行する時間が得られます) が、実際にはexit、何らかの方法でイベントが忍び込むことはできません。

doWorkいつでも割り込み可能にしたい場合は、ロジックを変更する必要があります。タイマーをより頻繁に作動させ、1 だけインクリメントします。exit はその間のいつでも実行できます。

于 2012-08-15T15:54:00.100 に答える