Keyboard macro press X each few secons while i press other keys - python

I'm playing a game that i need to press X each 1 sec, while i press other keyboard keys, but my hands are responding to pain
first i tried to do it on javascript:
const robot = require("robotjs");
function Sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function Main() {
console.log("running...");
await Sleep(2500);
PressTogether();
await Main();
}
function PressTogether() {
robot.keyTap("x");
}
Main();
also on python
import pyautogui
import time
print("hey")
while True:
time.sleep(2.5)
pyautogui.press("x")
time.sleep(2.5)
pyautogui.press("x")
print("bye")
Both are pressing the X each 2.5 seconds, but both got the same problem
it freezes my keyboard, i cant press any other key
why this is happen? how to fix?

Your keyboard doesn't react since by using sleep, you suspend the whole thread. It then is not able to do anything except blocking execution.
After sleeping for 2.5 seconds you just do sending out a keyboard action (pressing x key).
Then you are suspending your thread again.
Doing so, your process feels like being frozen because you effectively block your only one main thread most of the time.
You can overcome to that behavior by introducing another thread, which is responsible for your keyboard inputs taking them from stdin.
Here is one approach for python
import pyautogui
from time import sleep
from threading import Thread
from pynput.keyboard import Key, Listener
# a custom function that blocks for a moment and simulates key press
def akp_task():
print("hey")
while True:
time.sleep(2.5)
pyautogui.press("x")
time.sleep(2.5) #this could be omitted
pyautogui.press("x") #this could be omitted
# another function that handles manual key press inputs
def mkpi_task(key):
print('\nmanual pressed: {0}'.format(key))
if key == Key.delete:
# Stop listener
return False
# create a thread for auto key press
akp_thread = Thread(target=akp_task)
# run the thread
akp_thread.start()
# create another thread for your manual key press inputs
with Listener(on_press = mkpi_task) as listener:
listener.join()
References:
threading
manual key press handling

Related

GlobalHotKeys pynput not working not reacting to function keys

community. I'm trying to put together a quick hotkey script in python here.
For some reason it doesn't react to function keys, meaning the expression '<ctrl>+<F2>': function_1 doesn't work.
I was not able to find any clues in the official documentation or other examples online. Any thoughts?
Here is the script for testing.
from pynput import keyboard
def function_1():
print('Function 1 activated')
def function_2():
print('Function 2 activated')
with keyboard.GlobalHotKeys({
'<ctrl>+<F2>': function_1,
'<ctrl>+t': function_2}) as h:
h.join()
You could try setting up a generic handler to see what events are generated:
from pynput import keyboard
from pynput.keyboard import Controller
keyboard_controller = Controller()
# The event listener will be running in this block
with keyboard.Events() as events:
for event in events:
if event.key == keyboard.Key.esc:
break
else:
print(f"Received event {event}")
Running this script will then print the key press events, pressing Esc will exit the script:
Received event Press(key='a')
Received event Release(key='a')
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Press(key=Key.f3)
Received event Release(key=Key.f3)
Received event Press(key=Key.ctrl)
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Release(key=Key.ctrl)
Note that this won't handle hot keys and I don't see some key presses, e.g. to get Key.media_volume_up events I press Fn + F3.
You could potentially use keyboard.Events() handling to trigger your own hotkey equivalent.
I solved this issue by moving away from pynput Global Hotkeys and using just keyboard instead. I still don't understand the nature of this issue with global hotkeys not recognizing F1 key..
Here is the solution I used. I also added the passthrough of values with lambda function for each hotkey.
import keyboard
def func1(key):
print("F1 is pressed with value: ", key)
def func2(key):
print("F2 is pressed with value: ", key)
# define hotkeys
keyboard.add_hotkey('F1', lambda: func1("value1"))
keyboard.add_hotkey('F2', lambda: func2("value2"))
# run the program until 'F12' is pressed
keyboard.wait('F12')

The controller.type method does not work correctly inside the onpress event - Pynput

I'm building a rudimentary autocomplete for my work, and it was doing fine, until I ran into this problem.
See example
import threading
from pynput import keyboard
from pynput.keyboard import Controller
controll = Controller()
def on_press(key):
if(key == keyboard.Key.alt_l):
controll.type("RIGHT !!!")
if(key == keyboard.Key.delete or key == keyboard.Key.enter):
return False
def handleKey():
x = input('INPUT> ')
print(x)
handle = threading.Thread(target=handleKey)
handle.start()
controll.type("RIGHT !!!")
with keyboard.Listener(
on_press=on_press) as listener:
listener.join()
When you press alt, you will see the output with the wrong string;
It must be [2] RIGHT !, but it was pressed [2]ight
But note that this only happens with the method call inside the event.
Is this a known issue? Am I using it wrong? I'm really confused
I believe that only with python3 and pip3 install pynput, this will make the example work
Tested with Windows 10
I've had a similar issue when trying to press a key while listening to a key. By my experience it is next to impossible. I have even tried to use other libraries' press functions such as pyautogui and keyboard inside of the pynput on_press function.
def on_press(key):
if key = keyboard.Key.esc:
return False
else:
if len(words) > i:
# p = keyboard.KeyCode.from_char(words[i])
pyautogui.press(word[i]) // also used keyboard.press(word[i])
i += 1
else:
keyboard.Controller.press(keyboard.Key.space)
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
listener.join()
I feel like since the press action and the listen action are in different threads the program has to finish one before the other. At least that is what I gathered as I also have not found a solution to this. I also tried putting the press line after the listener.join() like this:
def on_press(key):
if key != keyboard.Key.esc:
return True
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
listener.join()
if len(words) > i:
# p = keyboard.KeyCode.from_char(words[i])
pyautogui.press(words[i])
i += 1
else:
keyboard.Controller.press(keyboard.Key.space)
However, this did not solve the issue either because the program finished after listening to one input and then pressing words[i] once.
Please let me know if you find something. However I may not be able to comment bc of low reputation.
I had a similar issue as well, and something like this worked for me (on Ubuntu 20.04):
from pynput import keyboard
import threading
from time import sleep
event_queue = []
event_to_callback = {
# put your own key-to-function mapping here
# keyboard.KeyCode.from_char('c') : deal_with_c
}
def process_events():
while True:
for c in event_queue:
event_to_callback[c]()
event_queue.clear()
sleep(0.2)
keyboard.Listener(on_press=lambda k: event_queue.append(k)).start()
threading.Thread(target=process_events, args=()).start()
This decouples the listener's thread from the controller's thread; any callbacks that invoke pynput.keyboard.Controller methods will now be executed on a separate thread, different from the one of the listener.

detect key press in python, where each iteration can take more than a couple of seconds?

Edit: The below answer to use keyboard.on_press(callback, suppress=False) works fine in ubuntu without any issues.
But in Redhat/Amazon linux, it fails to work.
I have used the code snippet from this thread
import keyboard # using module keyboard
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
break # if user pressed a key other than the given key the loop will break
But the above code requires the each iteration to be executed in nano-seconds. It fails in the below case:
import keyboard # using module keyboard
import time
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
print("sleeping")
time.sleep(5)
print("slept")
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
print("#######")
break # if user pressed a key other than the given key the loop will break
You can make use of event handlers in keyboard module to achieve the desired result.
One such handler is keyboard.on_press(callback, suppress=False):
Which invokes a callback for every key_down event.
You can refer more at keyboard docs
Here is the code you can try:
import keyboard # using module keyboard
import time
stop = False
def onkeypress(event):
global stop
if event.name == 'q':
stop = True
# ---------> hook event handler
keyboard.on_press(onkeypress)
# --------->
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
print("sleeping")
time.sleep(5)
print("slept")
if stop: # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
print("#######")
break # if user pressed a key other than the given key the loop will break
for people that might need this in the future, you can use keyboard.wait() which will basically wait untill the key gets pressed
keyboard.wait("o")
print("you pressed the letter o")
Do keep in mind that it blocks code execution after it. if you want to run code if the key is not being pressed i'd suggest doing
if keyboard.is_pressed("0"):
#do stuff
else:
#do other stuff
Edit: never mind, the other answer uses pretty much the same approach
This is what i could come up with, using the same "keyboard" module, see in-code comments below
import keyboard, time
from queue import Queue
# keyboard keypress callback
def on_keypress(e):
keys_queue.put(e.name)
# tell keyboard module to tell us about keypresses via callback
# this callback happens on a separate thread
keys_queue = Queue()
keyboard.on_press(on_keypress)
try:
# run the main loop until some key is in the queue
while keys_queue.empty():
print("sleeping")
time.sleep(5)
print("slept")
# check if its the right key
if keys_queue.get()!='q':
raise Exception("terminated by bad key")
# still here? good, this means we've been stoped by the right key
print("terminated by correct key")
except:
# well, you can
print("#######")
finally:
# stop receiving the callback at this point
keyboard.unhook_all()
You could use a thread
import threading
class KeyboardEvent(threading.Thread):
def run(self):
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
keyread = KeyboardEvent()
keyread.start()
This would run in parallel to anything in the main thread and be dedicated to listening for that key press essentially.

Listen For 2 Different Keystrokes in One Method (Pynput)

essentially my program listens for keystrokes and if it sees the "up" arrow pushed it starts printing the word test using a while loop that relies on "flag" being true. I would like the program to stop when the down key is pressed but I am unable to make that happen. I don't get any errors, it just doesn't stop.
Here is the code:
from pynput.keyboard import Key, Listener
flag = False
def doit():
while flag:
print("test")
def released(key):
global flag
if key == Key.up:
flag = True
doit()
elif key == Key.down:
print("stopped")
flag = False
with Listener(on_release=released) as listener:
listener.join()
When I press the down arrow "stopped" doesn't get printed so it seems like the if statement isn't being used at all. How can I fix this?
You're trying to do two things at once:
Listen for keyboard input
Do whatever doit() is supposed to do.
The following program starts doit() on a separate thread and thus allows the main-thread to continue listening for keystrokes.
from pynput.keyboard import Key, Listener
from threading import Thread
import time
flag = False
thread = None
def doit():
while flag:
print("test")
time.sleep(0.5)
def released(key):
global flag, thread
if key == Key.up:
flag = True
thread = Thread(target = doit)
thread.start()
elif key == Key.down:
print("stopped")
flag = False
if thread.is_alive():
thread.join()
with Listener(on_release=released) as listener:
listener.join()
thread.start() does not block execution, as doit() would. Only when calling thread.join() will the main-thread block until the thread is done. Notice that this depends on the main-thread setting flag = False, and without that, the thread might continue infinitely, and the main-thread would thus wait forever when calling thread.join().
There are a number of these kinds of problems that arise when stepping into the world of multithreading.

Python cannot cancel timer thread

I am trying to detect a long press event and cancel it if the button is released before the timer times out, however the timer never gets cancelled and fires if it is a short press or a long one:
from threading import Thread
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
def long_press():
print('long press')
Your code didn't run for me due to errors, but once I fixed those it worked fine:
This outputs long press after 1.5 seconds:
from threading import Timer
but = "down"
def long_press():
print('long press')
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
This outputs nothing:
from threading import Timer
but = "up"
def long_press():
print('long press')
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
I don't know what but is but my guess is that your but=='down' test might be the cause of the error.

Categories