在 PyQt4 中,有一项目标是使用单个 QThread 在不同时间运行两个(或更多)单独的方法。例如,希望 QThread 有时运行 play(),并在播放完成后,希望将 QThread 从此方法断开连接,以便将其连接到其他位置。本质上,希望 QThread 充当容器,用于任何希望与主进程并行运行的内容。
在启动 QThread 然后立即断开其连接的情况下遇到了问题,这会导致运行时出现奇怪的行为。在了解“竞争条件”的含义(或真正了解有关多线程的很多知识)之前,怀疑线程在断开连接之前尚未完全启动。为了克服这个问题,在 start() 和 disconnect() 调用之间添加了 5 毫秒的睡眠,并且它工作得很好。它工作得很好,但这不是正确的方式。
解决方案
可以使用以下方法来实现此功能,而无需调用 sleep():
- 在将 MouseRecord 对象移动到 QThread 之前,将 started 信号连接到 record() 方法。
- 在 QThread 开始后断开连接 started 信号。
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()
# 将 started 信号连接到 record() 方法
self.myThread.started.connect(self.mouseRecorder.record)
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.start()
# 在 QThread 启动后断开 started 信号的连接
self.myThread.started.disconnect()
def play(self):
self.stateLabel.setText("Status: Playback initated ...")
self.myThread.start()
# 在 QThread 启动后断开 started 信号的连接
self.myThread.started.disconnect()
通过这种方法,现在可以将 MouseRecord 对象移动到 QThread,并在 QThread 开始后断开 started 信号的连接,而无需使用 5 毫秒的睡眠。这将防止竞争条件并确保在断开连接之前完全启动 QThread。