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

Viewed 8864

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
4 Answers

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.

Related