我有一种情况,我想使用单个QThread在不同的时间运行两个(或更多)单独的方法。例如,我希望QThread有时会运行play()
,当我完成播放时,我想断开QThread与此方法的连接,以便我可以将其连接到其他地方。从本质上讲,我希望QThread能够作为我希望与主流程并行运行的任何东西的容器。
我遇到了启动QThread然后立即断开连接的问题,导致运行时出现奇怪的行为。在我发现“竞争条件”意味着什么(或者真正了解多线程)之前,我有一种潜在的怀疑,即线程在断开之前没有完全启动。为了解决这个问题,我在start()
和disconnect()
来电之间添加了5毫秒的睡眠,它就像一个魅力。它就像一个魅力,但它不是正确的方式。
如何在不调用sleep()
的情况下使用一个QThread实现此功能?
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()
答案 0 :(得分:0)
正确的方法是为每个操作创建新的QThread
,这样sleep
并且不需要断开连接。现在,即使您成功消除sleep
调用,也可能出现以下情况:
1)您运行play
,并断开插槽
2)您在record
完成之前运行play
。在这种情况下,先前创建的线程仍在运行并且:
如果线程已在运行,则此函数不执行任何操作。
(来自documentation)
如果你接受这种情况并且在某种程度上防止同时“播放”和“录制”,那么你应该像你自己写的那样:“当我完成游戏时,我想断开连接”。所以,不仅仅是在线程启动之后,而是在它完成之后。要做到这一点,试试这个:
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()
答案 1 :(得分:0)
您希望立即启动该线程并使用gui线程中的信号和插槽与MouseRecorder
实例进行通信。
您通过启动MouseRecorder
(您已连接信号以触发特定事件)来发信号通知QThread
实例。通常,如果您需要在工作线程中仅发生ONCE,则需要使用该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()
。