2

単一の QThread を使用して、異なる時間に 2 つ (またはそれ以上) の別々のメソッドを実行したい状況があります。たとえば、QThread をplay()時々実行したいのですが、プレイが終わったら、QThread をこのメソッドから切断して、別の場所に接続できるようにしたいと考えています。本質的には、メイン プロセスと並行して実行したいものすべてのコンテナーとして QThread を機能させたいと考えています。

QThread を開始してすぐに切断すると、実行時に奇妙な動作が発生するという問題に遭遇しました。「競合状態」が何を意味するのかを発見する前 (またはマルチスレッドについて十分に理解する前)、切断される前にスレッドが完全に開始されていないのではないかという疑念をひそかに持っていました。これを克服するために、呼び出しと呼び出しの間に 5 ミリ秒のスリープを追加しましたstart()disconnect()これは魅力的に機能します。それは魅力のように機能しますが、正しい方法ではありません。

への呼び出しを行わずに、1 つの QThread でこの機能を実装するにはどうすればよいsleep()ですか?

問題のコード スニペット:

def play(self):

        self.stateLabel.setText("Status: Playback initated ...")

        self.myThread.started.connect(self.mouseRecorder.play)
        self.myThread.start()
        time.sleep(.005)  #This is the line I'd like to eliminate

        self.myThread.started.disconnect()

完全なスクリプト:

class MouseRecord(QtCore.QObject):

    finished = QtCore.pyqtSignal()    

    def __init__(self):

        super(MouseRecord, self).__init__()        

        self.isRecording = False
        self.cursorPath = []

    @QtCore.pyqtSlot()  
    def record(self):

        self.isRecording = True
        self.cursorPath = []

        while(self.isRecording):

            self.cursorPath.append(win32api.GetCursorPos())
            time.sleep(.02)            

        self.finished.emit()

    def stop(self):

        self.isRecording = False

    @QtCore.pyqtSlot()    
    def play(self):

        for pos in self.cursorPath:
            win32api.SetCursorPos(pos)
            time.sleep(.02)        

        print "Playback complete!"
        self.finished.emit()            

class CursorCapture(QtGui.QWidget):

    def __init__(self):

        super(CursorCapture, self).__init__()

        self.mouseRecorder = MouseRecord()

        self.myThread = QtCore.QThread()

        self.mouseRecorder.moveToThread(self.myThread)
        self.mouseRecorder.finished.connect(self.myThread.quit)

        self.initUI()

    def initUI(self):

        self.recordBtn = QtGui.QPushButton("Record")
        self.stopBtn   = QtGui.QPushButton("Stop")
        self.playBtn   = QtGui.QPushButton("Play")        

        self.recordBtn.clicked.connect(self.record)
        self.stopBtn.clicked.connect(self.stop)
        self.playBtn.clicked.connect(self.play)

        self.stateLabel = QtGui.QLabel("Status: Stopped.")

        #Bunch of other GUI initialization ...

    def record(self):

        self.stateLabel.setText("Status: Recording ...")  

        self.myThread.started.connect(self.mouseRecorder.record)
        self.myThread.start()
        time.sleep(.005)        

        self.myThread.started.disconnect()

    def play(self):

        self.stateLabel.setText("Status: Playback initated ...")

        self.myThread.started.connect(self.mouseRecorder.play)
        self.myThread.start()
        time.sleep(.005)

        self.myThread.started.disconnect()
4

2 に答える 2

0

すぐにスレッドを開始しMouseRecorder、GUI スレッドからのシグナルとスロットを使用してインスタンスと通信する必要があります。

MouseRecorderを開始することでインスタンスにシグナルを送信してQThreadいます(特定のイベントをトリガーするためにシグナルを接続しています)。通常、ワーカー スレッドで 1 回だけ発生する必要がある場合は、その sig/slot 接続を使用する必要があります。moveToThreadそれ以外の場合は、通常、シグナルとスロット を取得する QObject とクロススレッド通信を行います。


代わりに、次のように記述します。

class MouseRecord(QtCore.QObject):

    def __init__(self):
        super(MouseRecord, self).__init__()        
        self.isRecording = False
        self.cursorPath = []

    @QtCore.pyqtSlot()  
    def record(self):
        self.isRecording = True
        self.cursorPath = []

        while(self.isRecording):
            #Needed, so that if a sigStop is emitted, self.isRecording will be able to be changed
            QApplication.processEvents()

            self.cursorPath.append(win32api.GetCursorPos())
            time.sleep(.02)   

    @QtCore.pyqtSlot()
    def stop(self):
        self.isRecording = False

    @QtCore.pyqtSlot()    
    def play(self):
        for pos in self.cursorPath:
            win32api.SetCursorPos(pos)
            time.sleep(.02)        
        print "Playback complete!"

class CursorCapture(QtGui.QWidget):

    sigRecord = QtCore.pyqtSignal()
    sigPlay = QtCore.pyqtSignal()
    sigStop = QtCore.pyqtSignal()

    def __init__(self):
        super(CursorCapture, self).__init__()

        self.mouseRecorder = MouseRecord()
        self.myThread = QtCore.QThread()

        self.mouseRecorder.moveToThread(self.myThread)

        self.sigRecord.connect(self.mouseRecorder.record)
        self.sigPlay.connect(self.mouseRecorder.play)
        self.sigStop.connect(self.mouseRecorder.stop)

        self.myThread.start()

        self.initUI()

    def initUI(self):
        self.recordBtn = QtGui.QPushButton("Record")
        self.stopBtn   = QtGui.QPushButton("Stop")
        self.playBtn   = QtGui.QPushButton("Play")        

        self.recordBtn.clicked.connect(self.record)
        self.stopBtn.clicked.connect(self.stop)
        self.playBtn.clicked.connect(self.play)

        self.stateLabel = QtGui.QLabel("Status: Stopped.")

        #Bunch of other GUI initialization ...

    def record(self):
        self.stateLabel.setText("Status: Recording ...")  
        self.sigRecord.emit()

    def play(self):
        self.stateLabel.setText("Status: Playback initated ...")
        self.sigPlay.emit()

    def stop(self):
        self.stateLabel.setText("Status: Recording Stopped...")
        self.sigStop.emit()

これにより、インスタンスが GUI スレッドからのシグナルを待機しQThreadている状態で、 を常に実行することができます (指示しない限り何もしないので問題ありません) 。MouseRecorder

の追加の必要性に注意してくださいQApplication::processEvents()

于 2013-08-29T11:52:05.757 に答える
0

正しい方法はQThread、アクションごとに new を作成することです。この方法では、sleepと の切断は必要ありません。現時点では、sleep通話を正常に除去したとしても、次のシナリオが考えられます。

1) を実行playし、スロットを切断します

2)終了するrecordplayに実行します。このような状況では、以前に作成されたスレッドがまだ実行されており、次のようになります。

スレッドがすでに実行中の場合、この関数は何もしません。

ドキュメントから)

ただし、このシナリオを受け入れて、「再生」と「録音」を同時に回避する場合は、「再生が終了したら、切断したい」と自分で書いたようにする必要があります。したがって、スレッドが開始された直後ではなく、スレッドが終了した後です。これを行うには、これを試してください:

1)self.mouseRecorder.finished.connect(self.myThread.quit)に変更self.mouseRecorder.finished.connect(self.threadFinished)

2) 実装:

def threadFinished(self):
        self.myThread.quit()
        self.myThread.started.disconnect()
于 2013-08-27T21:57:09.490 に答える