检测键是否被按住 - python

问题描述

希望大家一切都好...

我的用例

我有一个用例,我需要知道何时按下并按住某个键(特定键)。检测后的用例相当简单。当按键被释放时,发送一个信号来停止回调(我已经知道了)。

期望的行为

其实很简单。这是算法的粗略方案

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

HERE 是我根据 Isak 的建议写的作为尝试(和 MCVE)的内容。这在只有 1 个缺陷的情况下几乎可以完美运行。那就是它不会从程序开始就监听按键。

在开始实际检测到任何按键之前,由于某些未知原因需要一些时间。好消息是,一旦它第一次检测到按键,它就可以完美运行。

我错过了什么?

编辑 2

接受 answer by Isak 是正确的方法,但请阅读下面的 My Answer,它解释了该方法及其解决方案的可能问题。

解决方法

尝试检查特定键上的 the key_pressed 事件,直到事件变为 key_released。因此,当您检测到对键的点击时,您将执行您的代码,当它检测到该键被释放时,代码就会停止

,

我想出了为什么 My Approach 在开始 Listener 之前需要花费大量时间进行初始化。这是因为 while 循环没有任何 time.sleep() 调用,并且它可能会干扰系统(尽管我不希望它在自己的线程中运行时会发生这种情况,但可能是 {{1} }} 循环不会释放 while,因为它只是在循环中没有任何延迟地执行任何操作)。

我刚刚在 while 循环(外部循环)中添加了 GIL。任何延迟都会导致 time.sleep(0.2) 释放一段时间,GIL 线程将被处理并激活。

编辑:接受 Isak 的回答,因为这是正确的方法。