I have written a short program here designed to record and play back my mouse motions. I’ve implemented a simple GUI in PyQt and had it working in the past. Recently I decided to update the code to make it less ‘hacky’ by implementing QThread instead of using processEvents() in a main loop.
The code below executes, but behaves strangely. When running the code, here is what happens:
Input 1: I press Record
Console Output:
isRecording = TrueInput 2: I press Stop
Console Output:
Stopped!Input 3: I press Play
Console Output:
isRecording = TrueInput 4: I press Stop
Console Output:
Stopped!
Play!
Playback complete!
I am unclear why after pressing play the program attempts to record again, and only after pressing stop does it begin playback. Furthermore, after this stretch of weirdness, it continues to exhibit this similar type of output with minor deviations.
My guess is it has something to do with reassigning the thread signal started depending on whether or not I am calling the play or record member function of the CursorCapture class.
Any insight would be greatly appreciated.
Code
import win32api
import sys
import time
from PyQt4 import QtGui
from PyQt4.QtCore import Qt, QPoint
from PyQt4 import QtCore
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 = []
print "isRecording = " + str(self.isRecording)
while(self.isRecording):
#print "Recording"
self.cursorPath.append(win32api.GetCursorPos())
time.sleep(.02)
self.finished.emit()
print "Stopped!"
def stop(self):
self.isRecording = False
@QtCore.pyqtSlot()
def play(self):
print "Play!"
for pos in self.cursorPath:
#print "Playing"
win32api.SetCursorPos(pos)
time.sleep(.02)
print "Playback complete!"
self.finished.emit()
class CursorCapture(QtGui.QWidget):
def __init__(self):
super(CursorCapture, self).__init__()
self.isRecording = False
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.hBox = QtGui.QHBoxLayout()
self.hBox.addWidget(self.recordBtn)
self.hBox.addWidget(self.stopBtn)
self.hBox.addWidget(self.playBtn)
self.setLayout(self.hBox)
self.setWindowTitle("Cursor Capture")
self.show()
def record(self):
self.myThread.started.connect(self.mouseRecorder.record)
self.myThread.start()
def stop(self):
self.mouseRecorder.stop()
def play(self):
self.myThread.started.connect(self.mouseRecorder.play)
self.myThread.start()
def main():
qApp = QtGui.QApplication(sys.argv)
cursorCapture = CursorCapture()
sys.exit(qApp.exec_())
if __name__ == "__main__":
main()
After more investigating and playing around I discovered the problem. When reconnecting signals to slots, you have to make sure to
disconnect()the previous signals. What was happening was:Press record. Record signal is connected.
Press play. Since record signal wasn’t disconnected, it fires first. THEN, the play signal gets connected and executed. This type of behavior stacks, and this is why things were acting so strangely.