繁体   English   中英

在 linux 机器上检测 python 3 中按键的最简单方法是什么?

[英]What is the easiest way to detect key presses in python 3 on a linux machine?

现在我正在尝试用树莓派和 makey makey 编写一个小代码。 makey makey 是一块小板,当某些触点通电时可充当 USB 键盘。 我的问题是在 python 脚本中检测这些按键的最简单方法是什么。 我知道使用 GPIO 引脚会更容易,但现在我正在寻找这个。 我已经看到了一些例子,例如使用 msvcrt 中的 getch()(据我了解,它仅适用于 Windows)、使用 pygame.key 和使用 getKey。 哪一个论文最容易使用? 有没有什么可以检测到按下一个键和释放一个键?

伪代码:

import whatever needs importing    

if the "W" key is pressed:
   print ("You pressed W")

elif the "S" is pressed:
    print ("You pressed S")

等等。 谢谢。

这是一个简单的循环,它将 stdin 置于原始模式(禁用缓冲,因此您不必按 enter 键)以获取单个字符。 你应该做一些更聪明的事情(比如禁用它的with语句)但是你在这里明白了:

import tty
import sys
import termios

orig_settings = termios.tcgetattr(sys.stdin)

tty.setcbreak(sys.stdin)
x = 0
while x != chr(27): # ESC
    x=sys.stdin.read(1)[0]
    print("You pressed", x)

termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)    

我认为您必须循环检测 Python 中的密钥发布。

ETA 一些更多的解释:

在 Linux 上,程序的输入将被行缓冲 这意味着操作系统将缓冲输入直到它有整行,所以你的程序甚至不会看到用户键入的任何内容,直到用户也点击“输入”。 换句话说,如果您的程序期望用户键入“w”并且用户执行了此操作,则“w”将位于操作系统的缓冲区中,直到用户点击“enter”。 此时整行都被传送到您的程序,因此您将获得字符串“w\n”作为用户的输入。

您可以通过将 tty 置于原始模式来禁用它。 您可以使用 Python 函数tty.setcbreak执行此操作,该函数将调用 linux 中的 tty 驱动程序以告诉它停止缓冲。 我将sys.stdin参数传递给它,以告诉它我想为1关闭哪个流的缓冲。 所以在tty.setcbreak调用之后,上面的循环将为您提供用户按下的每个键的输出。

但是,一个复杂的问题是,一旦您的程序退出,tty 仍处于原始模式。 您通常会发现这并不令人满意,因为您没有获得现代终端设置提供的任何功能(例如当您使用控制或转义序列时)。 例如,请注意您可能无法使用ctrl-C退出程序。 因此,您应该在读取完输入字符后将终端重新置于熟化模式 termios.tcsetattr调用只是说“将终端放回我找到它的方式”。 它知道如何通过首先在程序开头调用termios.tcgetattr来做到这一点,它说“告诉我终端的所有当前设置”。

一旦理解了所有这些,您应该能够轻松地将功能封装在适合您的程序的函数中。

1 stdin是用户输入的流。 Wikipedia 可以告诉您更多关于标准流的信息。

使用一个好的轻量级模块curtsies你可以做这样的事情(取自他们的 examples/ 目录):

from curtsies import Input

def main():
    with Input(keynames='curses') as input_generator:
        for e in input_generator:
            print(repr(e))

if __name__ == '__main__':
    main()

所以按下键盘上的键会给你这样的东西:

'a'
's'
'KEY_F(1)'
'KEY_F(2)'
'KEY_F(3)'
'KEY_F(4)'
'KEY_F(5)'
'KEY_LEFT'
'KEY_DOWN'
'KEY_UP'
'KEY_RIGHT'
'KEY_NPAGE'
'\n'

bpython 使用curtsies作为终端相关内容的低级抽象。

解码输入的基本问题是,在不同的终端和终端仿真程序(如xtermgnome-terminals物理上相同的键会产生不同的键码序列。 这就是为什么需要知道应该使用哪些终端设置来解码输入的原因。 这样的模块有助于从那些血淋淋的细节中抽象出来。

由于您的问题表明您正在使用 Raspberry Pi 和 USB HID 键盘外围设备,但没有指定您是否将 Pi 配置为启动到运行脚本的文本或图形模式,我建议使用 libinput在任何一种情况下都可以使用。

您可以使用 libinput 的 python 绑定直接从内核读取键盘(和大多数其他输入设备)事件。

pip3 install python-libinput

该子系统的接口通过通常位于 /dev/input/ 中的字符设备公开。 它们由 udev 规则管理,这些规则为每个连接的输入设备创建一个或多个字符设备,并在例如连接或拔下 USB 键盘,或连接或断开蓝牙鼠标时动态添加和删除。

Libinput 为您处理打开和读取所有连接的输入设备的任务,以及分别在添加和移除设备时打开和关闭设备。

使用 python 中的 libinput 读取关键事件如下所示:

import libinput

def read_key_events():
    # init libinput
    li = libinput.LibInput(udev=True)
    li.udev_assign_seat('seat0')
    
    # loop which reads events
    for event in li.get_event():
    
        # test the event.type to filter out only keyboard events 
        if event.type == libinput.constant.Event.KEYBOARD_KEY:
        
            # get the details of the keyboard event
            kbev = event.get_keyboard_event()
            kcode = kbev.get_key() # constants in  libinput.define.Key.KEY_xxx
            kstate = kbev.get_key_state() # constants   libinput.constant.KeyState.PRESSED or .RELEASED 
            
            # your key handling will look something like this...
            if kstate == libinput.constant.KeyState.PRESSED:
                print(f"Key {kcode} pressed") 
                
            elif kstate == libinput.constant.KeyState.RELEASED:
                
                if kbev.get_key() == libinput.define.Key.KEY_ENTER:
                    print("Enter key released")
                    
                elif kcode == libinput.define.Key.KEY_SPACE:
                    print("Space bar released")
                else:
                    print(f"Key {kcode} released")

需要注意的一个小问题是,udev 通常配置为在 /dev/input/ 中创建的事件设备上设置权限,以仅允许属于特殊补充“输入”组成员的用户访问,因为允许不受限制访问原始用户键和鼠标输入将是一个主要的安全漏洞。 因此,如果在 libinput 初始化期间运行它会引发错误,您可能需要通过运行以下命令将输入添加到用户的补充组:

sudo usermod -G input -a "${USERNAME}"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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