boppreh / keyboard

Hook and simulate global keyboard events on Windows and Linux.
MIT License
3.8k stars 433 forks source link

on_press and on_release not executing if one is placed after another #152

Open pshe94 opened 6 years ago

pshe94 commented 6 years ago

Hi, I'm using on_pressand on_release to store any currently pressed keys. Unfortunately, if on_press is placed before on_release, on_release is not being executed. If I switch places of on_press with on_release, then on_press is not being executed. Because of it, I'm using hook as a workaround, in which I'm checking event_type and calling proper function.

pressed_keys = []

def add_key(key):
    pressed_keys.append(key)
    return

def remove_key(key):
    pressed_keys.remove(key)
    return

keyboard.on_press(add_key)
keyboard.on_release(remove_key)    #This is not being executed

Also I have one question: Is there a simpler way of checking in on_press_key if other keys are also currently pressed? modifiers is always empty.

pflaten commented 6 years ago

Hello,

I was trying to do the same exact thing with on_pressand on_release. It looks like the bug was introduced in a recent update (pip version 0.13.1). I downgraded to version 0.11.0 and that got rid of the bug.

To install version 0.11.0 I used the command: pip install keyboard==0.11.0

Hope this helps!

boppreh commented 6 years ago

I'm taking a look. Version 0.13 revamped a lot of the internals, so this is not too surprising. Still, sorry for the inconvenience.

@pshe94 did 'is_key_pressed' not working for you? Also, list of modifiers currently depends on OS, not all backends are reporting it.

pshe94 commented 6 years ago

@boppreh Did you mean is_pressed? If so, then yes, it works. But it requires to specify which key to check, but I would like to know if any other keys are also pressed. Currently I'm just using hook to store all pressed keys (I would use on_press and on_release, but I can't because of this bug).

boppreh commented 6 years ago

@pshe94 Yes, sorry, I meant is_pressed. I guess hooking and monitoring events works, but is wasteful, because is_pressed is doing the exact same thing behind the curtain. The pressed keys are available as keyboard._pressed_events, which is a map from scan codes to KEY_DOWN events. This is a good point, maybe this should be exposed somehow.

pshe94 commented 6 years ago

@boppreh You just made my code a little bit less complicated. Exposing all pressed key is a great idea. Thank you! 👍 Also I have one off-topic question, if you don't mind me asking here. There is no way of combining mouse button presses with keyboard keys? For example hotkey Ctrl+mouse X2? I can see that handling mouse is in a separate library, but then I would have to write some kind of wrapper for both of them

boppreh commented 6 years ago

@pshe94 I understand the need to combine mouse and keyboard events, and I have that on my roadmap. But I also think it's important to combine hotkeys with arbitrary events/conditions, like active program, window title, or time of day. I'm still thinking on what's the best way to present this flexibility in the API, and how to implement this in a performant way. Suggestions welcome.

For the moment I guess you can register a hotkey that adds a mouse hotkey on press, and removes it on release, on something similar.

Magnetization commented 5 years ago

This bug still exists in Version 0.13.3

arcadeforge commented 5 years ago

thanks for your awesome work on this project. I made a key 2 joy converter via uinput and experienced that using is_pressed is way to slow. Using the hooks via old version made the trick.

samomar commented 4 years ago

Bug still exists in Version: 0.13.4

Edit: possible workaround that works for me with multiprocessing

if you get Can't pickle local object error just create a separate file for these 2 functions then import them into the file you're starting the process from.

def handle_keyboard_press():
    def handle_press(e):
        print(e.name)
        print(e.event_type)

    while True:
        time.sleep(1)
        keyboard.on_press(handle_press)

def handle_keyboard_release():
    def handle_release(e):
        print(e.name)
        print(e.event_type)

    while True:
        time.sleep(1)
        keyboard.on_release(handle_release)
Process(target=handle_keyboard_press, args=[]).start()
Process(target=handle_keyboard_release, args=[]).start()

Another solution is to substitute on_press with hook and check if "down" in e.event_type

keyboard.hook(handle_press)
keyboard.on_release(handle_release)
Rasie1 commented 3 years ago

Recently I updated my Arch and this bug appeared. Multiple simultaneous hooks (including all functions such as on_press) don't work, I had to copy everything to one hook

rayanfer32 commented 3 years ago
        keyboard.on_release(trigger_event,suppress=False) 

        PASTE_WORD = "@@pst"
        keyboard.add_word_listener(PASTE_WORD, word_paste ,timeout=1)

        LOGIN_WORD = "@@cit"
        keyboard.add_word_listener(LOGIN_WORD, word_login ,timeout=1)

        LOGINPASS_WORD = "@@citp"
        keyboard.add_word_listener(LOGINPASS_WORD, word_loginpass ,timeout=1)

word_listener not getting activated if keyboard.on_release is uncommented System: Windows 8.1

rayanfer32 commented 3 years ago
        keyboard.on_release(trigger_event,suppress=False) 

        PASTE_WORD = "@@pst"
        keyboard.add_word_listener(PASTE_WORD, word_paste ,timeout=1)

        LOGIN_WORD = "@@cit"
        keyboard.add_word_listener(LOGIN_WORD, word_login ,timeout=1)

        LOGINPASS_WORD = "@@citp"
        keyboard.add_word_listener(LOGINPASS_WORD, word_loginpass ,timeout=1)

word_listener not getting activated if keyboard.on_release is uncommented System: Windows 8.1

Fixed it by placing the on_release() after all the word_listener()

jemiele1 commented 1 year ago

Bug still exists in 0.13.5.

When looking at the code for on_press and on_release, I got to thinking that the key event and the "or" operator were not correct, so I made a few changes. In the code below, I have commented out the original return lines and the new ones are directly below the commented lines.

def on_press(callback, suppress=False): """ Invokes callback for every KEY_DOWN event. For details see hook. """

return hook(lambda e: e.event_type == KEY_UP or callback(e), suppress=suppress)

return hook(lambda e: e.event_type == KEY_DOWN and callback(e), suppress=suppress)

def on_release(callback, suppress=False): """ Invokes callback for every KEY_UP event. For details see hook. """

return hook(lambda e: e.event_type == KEY_DOWN or callback(e), suppress=suppress)

return hook(lambda e: e.event_type == KEY_UP and callback(e), suppress=suppress)

For on_press, the "KEY_UP" became "KEY_DOWN" and the "or" become "and". For on_release, the "KEY_DOWN" became "KEY_UP" and the "or" become "and".

I tried it out on my system and the bug is gone for me!

Anyone want to give this a try? If it works,, on_press_key and on_release_key would need to have a similar change.

matteoem commented 1 year ago

as today, this issue is still persisting.

cacard commented 1 year ago

why? this is a simple problem, can fix it?

Gigabitten commented 3 months ago

@jemiele1 this seems to work for me! thank you greatly.