Multi - threading click macro / click recorder - python-3.x

I am working on a script that will listen to keystrokes till the 'q' button is pressed, afterwards it should stop the script and print out the mouse positions that were saved in 2 seconds intervals. I can't manage the threads and I am still learning this topic. Each time I run the code nothing happens but the process is running:
from pynput.keyboard import Listener
import pyautogui
from multiprocessing import Process
import time
mouse_positions = []
def func1():
while True:
time.sleep(2)
mouse_positions.append(pyautogui.position())
cordinates = []
quit_status = False
keystrokes = []
def on_press(key):
if "q" in str(key) :
print('q was pressed!')
exit("Stopped running")
#qprint(key)
keystrokes.append(key)
print(keystrokes)
#print(keystrokes)
if __name__ == '__main__':
p1 = Process(target=func1)
p1.start()
p1.join()
with Listener(on_press=on_press) as listener: # Create an instance of Listener
listener.join() # Join the listener thread to the main thread to keep waiting for keys
EDIT :
To anyone intrested, here is a click macro I built, script I built previously was more like mouse capture movement. The script below will record your mouse clicks and afterwards will replay them. Much better.
from pynput.keyboard import Listener
import pyautogui
from pynput import mouse
import time
x_pos = []
y_pos = []
both_pos = []
pressed_key = None
def on_click(x, y, button, pressed):
if pressed:
#print ("{0} {1}".format(x,y))
print(pressed_key)
if pressed_key == "1":
both_pos.append("{0}".format(x,y))
both_pos.append("{1}".format(x,y))
#print("test" + x_pos + y_pos)
print (x_pos + y_pos)
else:
pass
if pressed_key == 'q':
return False
def on_press(key):
print("To replay press 'q' , to stop recording press '1' , to record again press '1' .")
global pressed_key
if 'Key.esc' in str(key):
return False
if '1' in str(key):
pressed_key= None if pressed_key == '1' else '1'
if 'q' in str(key):
print("Replaying actions")
print(str(len(both_pos)))
for point in range(0,len(both_pos),2):
time.sleep(3)
print("clicking")
pyautogui.click(x=int(both_pos[point]),y=int(both_pos[point+1]))
print("done...")
return False
mouse_listener = mouse.Listener(on_click=on_click)
mouse_listener.start()
with Listener(on_press=on_press) as listener: # Create an instance of Listener
listener.join()
#print(mouse_listener.mouse_positions)

Hi you can use threading module.
I have created class MouseListener which inherit from threading.Thread class. Everything what you want to run put into run method. As thread stopper I used still_run attribute.
When you are typing, I pass to on_press function pressed key and mouse_listener. If q is pressed I set mouse_listener.still_run to False, what leads to stop the mouse listener.
mouse_positions I moved from global scope to MouseListener.
import threading
from pynput.keyboard import Listener
import pyautogui
import time
class MouseListener(threading.Thread):
still_run = True
mouse_positions = []
def run(self):
self.func()
def func(self):
while self.still_run:
time.sleep(2)
self.mouse_positions.append(pyautogui.position())
print(self.mouse_positions)
coordinates = []
quit_status = False
keystrokes = []
def on_press(key, mouse_listener):
print('kp')
if "q" in str(key):
print('q was pressed!')
mouse_listener.still_run = False
print(key)
exit("Stopped running")
keystrokes.append(key)
print(keystrokes)
print(keystrokes)
if __name__ == '__main__':
mouse_listener = MouseListener()
mouse_listener.start()
with Listener(on_press=lambda key: on_press(key, mouse_listener)) as listener: # Create an instance of Listener
listener.join()
print(mouse_listener.mouse_positions)

Related

exit loop pynput thread

Hello i'm trying to make a program to play snake using pynput.
When I press space the program runs but even if I press backspace i can't stop it, I know that I have to use threading but I don't figure how it works, thank you
my code :
from pynput import keyboard
from pynput.keyboard import Key, Controller
import threading
import time
kb = Controller()
def droite(key):
kb.press(Key.right)
kb.release(Key.right)
def gauche(key):
kb.press(Key.left)
kb.release(Key.left)
def haut(key):
kb.press(Key.up)
kb.release(Key.up)
def bas(key):
kb.press(Key.down)
kb.release(Key.down)
def on_press(key):
if key == Key.space:
print('yes')
droite(Key.right)
time.sleep(1.7)
haut(Key.up)
time.sleep(0.9)
gauche(Key.left)
time.sleep(2.15)
bas(Key.down)
time.sleep(1.9)
droite(Key.right)
time.sleep(2.15)
for i in range(255):
for i in range(6):
haut(Key.up)
time.sleep(0.1 )
gauche(Key.left)
time.sleep(2.1)
haut(Key.up)
time.sleep(0.1)
droite(Key.right)
time.sleep(2.1)
gauche(Key.left)
time.sleep(2.15)
bas(Key.down)
time.sleep(1.9)
droite(Key.right)
def on_release(key):
if key == Key.backspace:
print('no')
KeyboardInterrupt
return False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
th1 = threading.Thread(target=on_press)
th1.start()
th2 = threading.Thread(target=on_release)
th2.start()
th1.join()
th2.join()
I'm trying to stop the program even if I'm in a loop when I press a key

display the CMD console in a window in GUI - PySimpleGUI

I'm creating a GUI using PySimpleGUI and want to have a frame that displays the output prints of all the functions in the program (as in the cmd console).
I tried using (relevant code lines):
def hello():
print('hello')
layout = [[sg.Frame("Output console", [[sg.Text(" ", size=(0, 1), key='-OUTPUT-')]])]]
window['-OUTPUT-'].update(hello())
I can see the print in the console but not in the Frame in the GUI.
How can i do that?
To show the output like in CMD console, you should use the sg.Multiline, not the sg.Text which is just a label.
To redirect the output of stdout and stderr, set options reroute_stdout=True and reroute_stderr=True, also set option autoscroll=True to automatically scroll as more data added to the end of element.
Demo Code - GUI will update only back to window.read
import PySimpleGUI as sg
def hello():
print('hello')
frame_layout = [[sg.Multiline("", size=(80, 20), autoscroll=True,
reroute_stdout=True, reroute_stderr=True, key='-OUTPUT-')]]
layout = [
[sg.Frame("Output console", frame_layout)],
[sg.Push(), sg.Button("Say Hello")],
]
window = sg.Window("Title", layout, finalize=True)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "Say Hello":
hello()
window.close()
To update GUI in realtime, you can call update(value, append=True) and window.refresh, not to use the print statement, or
Following code show the way to print to sg.multiline immediately. To avoid stop GUI working for long printing, mutlithread required here. It will not work for real time printing during event loop because loop is working on your printing statement, then back to window.read after it done.
It is much complex, not sure if any bug.
import sys
from time import sleep
from datetime import datetime
import threading
import PySimpleGUI as sg
def print_task():
global printing
while printing:
print(datetime.now().strftime("%H-%M-%S"), datetime.now().strftime("%H-%M-%S"))
sleep(0.5)
class Unbuffered(object):
def __init__(self, window):
self.window = window
def write(self, data):
self.window.write_event_value("OUT", data)
def writelines(self, datas):
self.window.write_event_value("OUT", ''.join(datas))
frame_layout = [[sg.Multiline("", size=(80, 20), autoscroll=True, key='-OUTPUT-')]]
layout = [
[sg.Frame("Output console", frame_layout)],
[sg.Push(), sg.Button("Print"), sg.Button('Stop')],
]
window = sg.Window("Title", layout, finalize=True)
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout = Unbuffered(window)
sys.stderr = Unbuffered(window)
printing = False
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'Print':
if not printing:
printing = True
threading.Thread(target=print_task, daemon=True).start()
elif event == 'Stop':
printing = False
elif event == "OUT":
window['-OUTPUT-'].update(values["OUT"], append=True)
printing = False
sys.stdout, sys.stderr = old_stdout, old_stderr
window.close()

Stop mouse event when pressing key

My code uses mouse events to send messages but I have no idea how to stop using an f1 key. I didn't find a stop () method as in Listener () in Events ().
import pynput
import pyautogui
import time
from pynput import mouse
from pynput import keyboard
running = True
mouse_event = mouse.Events()
def on_press(key):
global running
if key == keyboard.Key.f1:
running = False
print('end')
return False
def msg():
mouse_event.start()
for event in mouse_event:
if running:
if isinstance(event,mouse.Events.Click):
if not event.pressed:
pyautogui.typewrite('hello world\n',interval=0.009)
else:
time.sleep(1)
return False
while 1:
print("Choose the desired option")
print("1. Message")
option = int(input("Option:"))
if option == 1:
running = True
with keyboard.Listener(on_press=on_press) as listener:
if running:
msg()
elif not listener.running:
time.sleep(2)
break
Is there a way to interrupt a mouse event with the press of a key?
One attempt was to pass the method as on_press to Events () does not work.
I've had to do something similar in the past and I ended up using threading to have one thread listen for the mouse/keyboard input while the other runs my main program.
import time
from pynput import keyboard
from threading import Thread
def exit_program():
def on_press(key):
if str(key) == 'Key.f1':
main.status = 'pause'
user_input = input('Program paused, would you like to continue? (y/n) ')
while user_input != 'y' and user_input != 'n':
user_input = input('Incorrect input, try either "y" or "n" ')
if user_input == 'y':
main.status = 'run'
elif user_input == 'n':
main.status = 'exit'
exit()
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
def main():
main.status = 'run'
while True:
print('running')
time.sleep(1)
while main.status == 'pause':
time.sleep(1)
if main.status == 'exit':
print('Main program closing')
break
Thread(target=main).start()
Thread(target=exit_program).start()

Key Stroke logging: Records both pressed and released

Here Is the keylogger Code:
''' from pynput.keyboard import Key, Listener
import logging
log_dir = r"/Users/user/Desktop/submit/Keylogger.txt "
logging.basicConfig(filename=(log_dir + "keyLog.txt"), level=logging.DEBUG, format='%(asctime)s: %(message)s')
def on_press(key):
logging.info(str(key))
def on_release(key):
print('{}'.format(key))
if str(key) == 'Key.esc':
print('Exiting...')
return False
with Listener(on_press=on_press) as listener:
listener.join() '''
When ever I run it, it works fine but for one thing: It records in double, so ill see A A and B B for one word. How can I fix this? (python3, MacOS)

curses wrapper in multithread does not restore screen after quiting

I'm trying to develop a multithreaded program with a TUI interface . Basically I have a main Loop deciding what to do and some task (like the TUI or reading data from a queue and processing it ) are running in separate thread .
My TUI is using curses and is a thread derived class that look like this (i removed non essential code for clarity) :
import threading
from time import sleep
import curses
import logging
from curses.textpad import Textbox, rectangle
from datetime import datetime
import re
from curses import panel
import os
import sys
class GenericTUI(threading.Thread):
def __init__(self, logger=logging.getLogger()):
threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=True)
self.keyPressedList = list()
self.alive = True
self._myStdscr = None
self.title = ""
self.logger = logger
self.lock = threading.Lock()
def run(self):
curses.wrapper(self.main)
curses.nocbreak()
curses.echo()
curses.noraw()
sys.exit(0)
def main(self,stdscr):
self._myStdscr = stdscr
self._myStdscr.nodelay(True)
self._myStdscr.keypad(True)
self._myStdscr.box()
while self.alive:
sleep(0.4)
try :
key = self._myStdscr.getkey()
if re.match('[A-Z_\+\-\*/]', key):
self.keyPressedList.append(key)
except Exception as e:
## ignoring no key presssed
pass
try :
with self.lock :
self._myStdscr.clear()
self._myStdscr.addstr(1, 2, str(datetime.now())+" "+ sys.argv[0] +" "+self.title )
### printing other things
self._myStdscr.refresh()
except Exception as e:
self.logger.error(e, exc_info=True)
continue
self._myStdscr.clear()
self._myStdscr.keypad(0)
def getKeyPressed(self):
if self.keyPressedList :
return self.keyPressedList.pop()
else :
return None
def stop(self):
self.alive = False
def updateTitle(self,title):
with self.lock : self.title = title
if __name__ == "__main__":
## the main is used for some test when the lib is called directly
testGUI = GenericTUI()
alive = True
testGUI.logger.addHandler(logging.StreamHandler())
testGUI.logger.setLevel(logging.DEBUG)
testGUI.start()
while alive :
testGUI.updateTitle('title %s'%str(datetime.now() ))
k = testGUI.getKeyPressed()
if k is not None:
if k=='Q' :
alive = False
else :
testGUI.addMessage('unknown key %s'%k , maj=True)
sleep(0.1)
the main loop of my program instantiate and start a genericTUI object and get keypressed from it or set value to display.
But when i quit the program , my terminal is in a funny state even if I used the curses wrapper function or try to reset manually using curses.nocbreak() and others.
I can't figure what I did wrong ? Am i mistaken using curses inside a thread ??
I found the answer but puting it in the comment section makes it hard to read . So I also put it here :
the curses wapper does not like the thread in daemon mode :
so the following code works fine and restore the terminal in a correct state :
class GenericTUI(threading.Thread):
def __init__(self, logger=logging.getLogger()):
threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=False)
self.keyPressedList = list()
and in the stop function adding a curses.endwin() helps :
def stop(self):
curses.endwin()
self.alive = False
hope it helps other
#WesModes I use the stop to have a clean way to stop the TUI. The endwin is cleaning the screen .
example :
testGUI=GenericTUI()
alive = True
testGUI.start()
while alive:
try :
k = testGUI.getKeyPressed()
if k is not None:
if k== 'Q':
testGUI.stop()
alive = False

Resources