François M.
François M.

Reputation: 4278

Listening for specific keys with pynput

I'd like my code to listen for user input, and do something if key c is pressed, and something else if key v is pressed.

I've managed to do it using global, but it feels like an ugly hack :

from pynput import keyboard

def on_press(key):
    try:
        global user_input 
        if key.char == "c":
            user_input = "c"
        elif key.char == "v":
            user_input == "v"
    except AttributeError:
        pass

def on_release(key):
    if key == keyboard.Key.esc:
        # Stop listener
        return False

def wait_for_user_input():
    global user_input
    listener = keyboard.Listener(on_press=on_press, on_release=on_release)
    listener.start()
    user_input = 0
    while user_input == 0:
        time.sleep(0.5)
        if user_input == "c":
            # do something
            listener.stop()
            break
        elif user_input == "v":
            # do something else
            listener.stop()
            break
    # other stuff

wait_for_user_input()

Is there a better way to do it ? (Maybe by having the listener stop & return the values c or v in on_press() ? If so, I couldn't find how to do it.)

Also : since wait_for_user_input() will be called multiple times, would it be better to not start and stop the listener repetitively, and instead have it start once and stop once ?

Upvotes: 2

Views: 5759

Answers (1)

furas
furas

Reputation: 142641

You can put functions directly in on_press and then you don't need while loop. You may need only listener.join() which will wait for listener.stop()

from pynput import keyboard

def on_press(key):
    try:
        if key.char == "c":
            # do something
            return False  # Stop listener
        elif key.char == "v":
            # do something else
            return False  # Stop listener
    except AttributeError as ex:
        print(ex)

def on_release(key):
    if key == keyboard.Key.esc:
        # Stop listener
        return False

def wait_for_user_input():
    listener = keyboard.Listener(on_press=on_press, on_release=on_release)
    listener.start()
    listener.join() # wait till listener will stop
    # other stuff        

EDIT:

If you need run functions which result you need in other functions then you may stay with global user_input but you can write it little different.

from pynput import keyboard

def on_press(key):
    global user_input

    try:
        if key.char in ("c", "v"):
            user_input = key.char
            return False  # Stop listener
    except AttributeError as ex:
        print(ex)

def on_release(key):
    if key == keyboard.Key.esc:
        return False  # Stop listener

def wait_for_user_input():
    listener = keyboard.Listener(on_press=on_press, on_release=on_release)
    listener.start()
    listener.join() # wait till listener will stop

    if user_input == "c":
        # do something
    elif user_input == "v":
        # do something else
    else:
        print('You pressed ESC ?')

EDIT: If you use Windows then you could use msvcrt.getch which gives shorter and nicer code.

from msvcrt import getch

def wait_for_user_input():

    while True:
        user_input = getch()
        if user_input == "c":
            print('selected: c')
            break
        elif user_input == "v":
            print('selected: v')
            break
        elif user_input == escape:
            print('You pressed ESC ?')
            break

wait_for_user_input()

For Linux should be similar function getch() but with longer code.

See also module getch but I didn't check it.

Upvotes: 4

Related Questions