I'm currently coding a macro software where it records mouse movement, click and keyboard input. I'm using Tkinter for the GUI and pynput to detect input. So, in my gui I have two buttons, one for playing the record and one to record mouse and keyboard inputs. When i press record, i have to re-click on it to stop the record. But, pynput function loop make my Tkinter GUI freeze, so i set a thread to make my Tkinter window working while pynput function are running. The problem is when I stop the recording and then I play it, the record is 0.5x slower than the moment i was recording. I think it's because of time.time(), which is imprecise because of the thread. I'm actually blocked and i don't know how to fix this.
import threading
from tkinter import *
from tkinter.ttk import *
from macro import startRecord, stopRecord, playRec
# Window Setup
window = Tk()
window.title("MacroRecorder")
window.geometry("350x200")
def startRecordingAndChangeImg():
global stopBtn
recordBtn.pack_forget()
stopBtn = Button(window, text="stop", command=stopRecordingAndChangeTxt)
stopBtn.pack(side=RIGHT, padx=50)
threading.Thread(target=startRecord).start()
def stopRecordingAndChangeImg():
global recordBtn
stopBtn.pack_forget()
recordBtn = Button(window, text="record", command=startRecordingAndChangeTxt)
recordBtn.pack(side=RIGHT, padx=50)
stopRecord()
# Play Button
playBtn = Button(window, text="play", command=playRec)
playBtn.pack(side=LEFT, padx=50)
# Record Button
recordBtn = Button(window, text="record", command=startRecordingAndChangeTxt)
recordBtn.pack(side=RIGHT, padx=50)
window.mainloop()
from pynput import mouse, keyboard
from pynput.mouse import Button
from pynput.keyboard import Key
import time
import os
import json
mouseControl = mouse.Controller()
keyboardControl = keyboard.Controller()
def startRecord():
global start_time
global mouse_listener
global keyboard_listener
global macroEvents
macroEvents = {'events': []}
start_time = time.time()
mouse_listener = mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll)
keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release)
print('record started')
with mouse_listener, keyboard_listener:
mouse_listener.join()
keyboard_listener.join()
def stopRecord():
mouse_listener.stop()
keyboard_listener.stop()
json_macroEvents = json.dumps(macroEvents, indent=4)
with open(os.path.join("C:/Users/***/Desktop/autre/python project/macro-recorder/data/test.json"), "w") as macroRecord:
macroRecord.write(json_macroEvents)
print('record stopped')
def on_move(x, y):
global start_time
macroEvents["events"].append({'type': 'cursorMove', 'x': x, 'y': y, 'timestamp': time.time() - start_time})
start_time = time.time()
# I use the same methods for each listener on_click, on_scroll, on_press and on_release, I reset the timer to 0
def playRec():
print('record playing')
for i in range(len(macroEvents["events"])):
time.sleep(macroEvents["events"][i]["timestamp"])
if macroEvents["events"][i]["type"] == "cursorMove":
mouseControl.position = (macroEvents["events"][i]["x"], macroEvents["events"][i]["y"])
elif macroEvents["events"][i]["type"] == "leftClickEvent":
if macroEvents["events"][i]["pressed"] == True:
mouseControl.press(Button.left)
else:
mouseControl.release(Button.left)
elif macroEvents["events"][i]["type"] == "rightClickEvent":
if macroEvents["events"][i]["pressed"] == True:
mouseControl.press(Button.right)
else:
mouseControl.release(Button.right)
elif macroEvents["events"][i]["type"] == "middleClickEvent":
if macroEvents["events"][i]["pressed"] == True:
mouseControl.press(Button.middle)
else:
mouseControl.release(Button.middle)
elif macroEvents["events"][i]["type"] == "scrollEvent":
mouseControl.scroll(macroEvents["events"][i]["dx"], macroEvents["events"][i]["dy"])
elif macroEvents["events"][i]["type"] == "keyboardEvent":
keyToPress = macroEvents["events"][i]["key"] if 'Key.' not in macroEvents["events"][i]["key"] else special_keys[macroEvents["events"][i]["key"]]
if macroEvents["events"][i]["pressed"] == True:
keyboardControl.press(keyToPress)
else:
keyboardControl.release(keyToPress)
I'm expecting to have mu GUI not freezing and pynput function working on his fullest.
Thanks in advance for you responses.