moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.79k stars 248 forks source link

Is there a non-blocking fashion for pynput.keyboard.Listener.canonical? #456

Closed Ynjxsjmh closed 2 years ago

Ynjxsjmh commented 2 years ago

In Monitoring the keyboard it introduces two ways to use pynput.keyboard.Listener:

# Collect events until released
with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()

# ...or, in a non-blocking fashion:
listener = keyboard.Listener(
    on_press=on_press,
    on_release=on_release)
listener.start()

In Global hotkeys, there is only one way to use pynput.keyboard.Listener.canonical:

from pynput import keyboard

def on_activate():
    print('Global hotkey activated!')

def for_canonical(f):
    return lambda k: f(l.canonical(k))

hotkey = keyboard.HotKey(
    keyboard.HotKey.parse('<ctrl>+<alt>+h'),
    on_activate)
with keyboard.Listener(
        on_press=for_canonical(hotkey.press),
        on_release=for_canonical(hotkey.release)) as l:
    l.join()

It can capture as many hotkey press as I try. However, when I try to use the following non-blocking fashion,

from pynput import keyboard

def on_activate():
    print('Global hotkey activated!')

def for_canonical(f):
    return lambda k: f(l.canonical(k))

hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+<alt>+h'),
                         on_activate)

l = keyboard.Listener(on_press=for_canonical(hotkey.press))
l.start()
l.join()

It only captures the first hotkey press, what's wrong here

moses-palmer commented 2 years ago

Thank you for your report.

In your second example, you do not seem to have a listener for on_release. A hot key will only trigger when it transitions into the state all keys pressed, and when you never tell it that keys are released, it will remain in the triggered state.