检测按键是否被按住 - Python

4

我的使用场景

我需要知道何时按下并按住一个(特定的)键。检测后的使用场景非常简单。当松开该键时,发送信号以停止回调(我已经知道如何做了)。

期望行为

以下是算法大致的示意图:

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

我应该提到,任何会阻塞执行的监听器都可以使用,因为它将在自己的线程中运行(已经在线程中运行pynput.keyboard.Listener,所以不是问题)。

我尝试了什么

我使用了pynput及其pynput.keyboard.Listener来检测按键并相应地调用回调函数,但我无法使其能够检测按键是否被按住

当前的解决方案大致如下:

# 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()` 
    

我有强烈的感觉,通过添加 on_release=another_callback_that_handles_releases(在pynput.keyboard.listener内可用),我可以使这个工作起来。
也许通过存储最后一个已知的按下的按键,并检查释放的键是否与之前按下的热键相同,但我不确定该如何去做,而且那样行得通吗?
然后我决定尝试使用另一个库 keyboard。我为此编写了以下代码,它可以检测到按键被按住的情况。下面的代码实现了几乎接近我想要的效果:
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)

这种解决方案的问题是,它不太适用于OSX和Ubuntu。而且使用特殊键时会有一些问题。此外,我将热键存储为pynput.keyboard.Key.f7(例如)或pynput.keyboard.KeyCode(char='s') # for character keys,这些枚举的值与keyboard用于扫描键ID(使用keyboard.hook())的值不同。
最终问题
如何检测按键被按下并保持?我希望使用pynput来实现这个功能,因为代码库的其余部分都在使用它,但'keyboard也可以。 我觉得使用on_press=a_callbackon_release=another_callback可以实现,但我不完全确定。最后,最好是跨平台的解决方案(根据platform.system()的值使用三个不同的函数是可以接受的)。
你会怎么做?
编辑1 这里是我在Isak建议后尝试编写的(MCVE)。这个解决方案几乎完美地工作,只有一个缺陷。即它不能立即从程序启动时开始监听按键。由于某种未知的原因,它需要一些时间才能开始实际检测任何按键。好消息是,一旦它第一次检测到按键,它就可以无缝地工作。
我错过了什么?
2个回答

2
尝试检测特定键的按下事件,直到该事件变为“key_released”。因此,当您检测到按键单击时,执行您的代码,当它检测到该键释放时,代码停止。

这是我写的一个尝试(和MCVE)。它几乎完美地工作,只有一个缺陷。那就是它在程序启动时不能正确监听按键。由于某种未知原因,它需要一些时间才能开始实际检测任何按键。好消息是,一旦它第一次检测到按键,它就无缺陷地工作了。我错过了什么? - P S Solanki
1
@PSSolanki 可能由于导入键盘模块所需的时间而导致延迟。 - Sid
感谢回复,Sid!但实际上我在脚本中并没有 import keyboard。关于 pynput.keyboard.Listener 实例,我有一个更大的脚本,导入了很多东西,包括这个 mcve 中的内容,而且那个脚本可以立即运行。@Sid - P S Solanki

1
我找到了我的方法启动Listener前初始化需要很长时间的原因。这是因为while循环没有任何time.sleep()调用,可能会干扰系统(虽然我不希望发生这种情况,因为它在自己的线程中运行,但可能while循环不会释放GIL,因为它只是在循环中没有任何延迟地做着什么)。
我只是在while循环(外部循环)中添加了time.sleep(0.2)。任何延迟都可以释放GIL一段时间,使Listener线程得到处理并变为活动状态。
编辑:接受Isak的答案作为正确的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接