简体   繁体   中英

Detecting if a key is HELD down - python

Hope everyone is doing well...

my Use case

I have a use case where I need to know when a key (a specific key) is pressed and held down. The use case after detection is fairly easy. When the key is released, send a signal to stop the callback (which I know already).

desired behavior

It's quite simple. here is a rough scheme of how the algo looks like

def the_callback():
    if key_held == the_hotkey:
        someObj.start()  # this class Obj works totally well so no issues here on
    elif key_released == the_hotkey:
        someObj.stop()
    else:
        # we don't care. continue looking for Keyboard events

# here any kinda listener or just a loop which passes events to the callback

I should mention that any kinda listener which blocks the execution is okay as it will run in its own thread (already running pynput.keyboard.Listener in a thread so not a problem)

What I've tried

I used pynput and its pynput.keyboard.Listener to detect key-presses and invoke callbacks accordingly but I couldn't make that work to detect when a key is HELD down.

the current solution looks roughly like:

# not real code. just rough scheme
def on_pressed(key):
    if key == my_hotkey:
        if running_already:  # this part works well already
            obj.stop()
        else:
            obj.start()
    else:
        # we don't care

with pynput.keyboard.Listener(on_press=on_pressed) as listener:
    listener.join()  # blocking call until SystemExit, `return False` from callback or `listener.stop()` 
    

I have a very strong feeling that I can make this work by adding on_release=another_callback_that_handles_releases (available within pynput.keyboard.listener ).

Perhaps by storing the last known pressed keystroke, and checking if the key released was the same as the hotkey which was pressed earlier but I'm not sure how would I go about it and can that even work?

Then I decided to give keyboard (different lib) a go. I wrote the below code for the same which can detect keys being held down. This below code achieves almost nearly what i want

import keyboard as kb, time

while 1:
    while kb.is_pressed('q'):
        print('Key is held')
        time.sleep(0.5)  # sleep added just to stop it from spamming the stdout
        
    else:
        print('No it\'s Not')
        time.sleep(0.5)

The issue with solution is, it's not very well suited for OSX and ubuntu. and it has some issues working with special keys. Moreover I have the hotkey stored as pynput.keyboard.Key.f7 (for eg) or pynput.keyboard.KeyCode(char='s') # for character keys and these enums have different values than what keyboard uses to scan key IDs (using keyboard.hook() ).

the final question

How should I go about detecting a key being HELD down. I'd prefer to achieve this using pynput as the rest of the code base uses it but 'keyboard is fine too. again I have a feeling that using on_press=a_callback and on_release=another_callback this might be achieved but I'm not entirely sure about it. and lastly the solution is preferred to be cross platform. (I'm fine with using three different functions depending on value of platform.system() )

How would you go about achieving it?

Any help or suggestion is appreciated:)

Thanks.

EDIT-1

HERE is what I wrote as an attempt (and MCVE) after suggestion by Isak. This works almost perfectly with just 1 flaw. And that is that it doesn't listen to keypresses right from the program start.

It takes some time for some unknown reason before it starts to actually detects any keypress. Good thing is that once it detects the keypress for the first time, it works flawlessly.

What am I missing there?

Edit 2

Accepting answer by Isak as that is the correct approach but do read My Answer below it which explains a probable issue with the approach and its solution.

Try to check for the key_pressed event on the specific key until the event becomes key_released . So when you detect a click on the key you execute your code and when it detects the release of that key the code stops

I figured out why My Approach was taking a lot of time to initialize before starting the Listener . It was because of the while loop which didn't have any time.sleep() calls and it was probably messing with the system (although I wouldn't expect that to happen as it runs in its own thread but probably the while loop doesn't release the GIL as it's just in the loop doing literally nothing without any sort of delay).

I just added time.sleep(0.2) inside the while loop (the outer one). Any delay would do as that would release the GIL for some time and the Listener thread would be processed and made active.

Edit: Accepting the answer by Isak as accepted as that is the correct approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM