2

我有一种情况,我想使用单个 QThread 在不同时间运行两个(或更多)单独的方法。例如,我希望 QThreadplay()有时运行,当我玩完时,我想断开 QThread 与此方法的连接,以便我可以将它连接到其他地方。本质上,我希望 QThread 充当我想与主进程并行运行的任何东西的容器。

我遇到了启动 QThread 然后立即断开它会导致运行时出现奇怪行为的问题。在我发现“竞态条件”是什么意思(或者真正了解多线程)之前,我偷偷怀疑线程在断开连接之前还没有完全启动。start()为了克服这个问题,我在and调用之间添加了 5 毫秒的睡眠,disconnect()它就像一个魅力。它就像一个魅力,但它不是正确的方式。

如何在不调用 QThread 的情况下使用一个 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(您已连接信号以触发特定事件)来向实例发出信号。通常,如果您有需要在工作线程中仅发生一次的事情,您将希望使用该 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()

这将允许QThread您的实例始终运行(这不是问题,因为除非您告诉它,否则它不会做任何事情)MouseRecorder等待来自您的 gui 线程的信号。

请注意对QApplication::processEvents().

于 2013-08-29T11:52:05.757 回答
0

正确的方法是QThread为每个动作创建新的,这样sleep就不需要断开连接。现在,即使你成功消除了sleep呼叫,也可能出现以下情况:

1)你运行play,并断开插槽

2)你跑record之前play已经完成了。在这种情况下,先前创建的线程仍在运行,并且:

如果线程已经在运行,这个函数什么也不做。

(来自文档

但是,如果您接受这种情况并以某种方式同时防止“播放”和“录制”,那么您应该按照您自己写的那样做:“当我完成播放时,我想断开连接”。所以,不仅仅是在线程启动之后,而是在它完成之后。为此,请尝试以下操作:

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 回答