简体   繁体   English

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

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

Right now I'm trying to make a small code with a raspberry pi and and a makey makey.现在我正在尝试用树莓派和 makey makey 编写一个小代码。 The makey makey is a small board that acts as a usb keyboard when certain contacts are powered. makey makey 是一块小板,当某些触点通电时可充当 USB 键盘。 My question is what is the easiest way to detect those keypresses inside a python script.我的问题是在 python 脚本中检测这些按键的最简单方法是什么。 I understand using the GPIO pins would be easier, but right now I'm looking for this.我知道使用 GPIO 引脚会更容易,但现在我正在寻找这个。 I have seen examples such as using using getch() from msvcrt (which from what I understand is windows only,) using pygame.key, and using getKey.我已经看到了一些例子,例如使用 msvcrt 中的 getch()(据我了解,它仅适用于 Windows)、使用 pygame.key 和使用 getKey。 Which of theses is easiest to use?哪一个论文最容易使用? Are there any that can detect a key being pressed and a key being released?有没有什么可以检测到按下一个键和释放一个键?

Pseudo code:伪代码:

import whatever needs importing    

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

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

and so on.等等。 Thanks.谢谢。

This is a simple loop that will put stdin in raw mode (disabling buffering so you don't have to press enter) to get single characters.这是一个简单的循环,它将 stdin 置于原始模式(禁用缓冲,因此您不必按 enter 键)以获取单个字符。 You should do something smarter (like a with statement to disable it) but you get the idea here:你应该做一些更聪明的事情(比如禁用它的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)    

I think you'd have to loop to detect key releases in Python.我认为您必须循环检测 Python 中的密钥发布。

ETA some more explanation: ETA 一些更多的解释:

On Linux, input to your program will be line buffered .在 Linux 上,程序的输入将被行缓冲 This means that the operating system will buffer up input until it has a whole line, so your program won't even see anything the user typed until the user also hits 'enter'.这意味着操作系统将缓冲输入直到它有整行,所以你的程序甚至不会看到用户键入的任何内容,直到用户也点击“输入”。 In other words, if your program is expecting the user to type 'w' and the user does this, 'w' will be sitting in the OS's buffer until the user hits 'enter'.换句话说,如果您的程序期望用户键入“w”并且用户执行了此操作,则“w”将位于操作系统的缓冲区中,直到用户点击“enter”。 At this point the entire line is delivered to your program so you will get the string "w\n" as the user's input.此时整行都被传送到您的程序,因此您将获得字符串“w\n”作为用户的输入。

You can disable this by putting the tty in raw mode .您可以通过将 tty 置于原始模式来禁用它。 You do this with the Python function tty.setcbreak which will make a call down the tty driver in linux to tell it to stop buffering.您可以使用 Python 函数tty.setcbreak执行此操作,该函数将调用 linux 中的 tty 驱动程序以告诉它停止缓冲。 I passed it the sys.stdin argument to tell it which stream I wanted to turn buffering off for 1 .我将sys.stdin参数传递给它,以告诉它我想为1关闭哪个流的缓冲。 So after the tty.setcbreak call, the loop above will give you output for every key the user presses.所以在tty.setcbreak调用之后,上面的循环将为您提供用户按下的每个键的输出。

A complication, though, is that once your program exits, the tty is still in raw mode.但是,一个复杂的问题是,一旦您的程序退出,tty 仍处于原始模式。 You'll generally find this unsatisfying since you don't get any of the power that modern terminal settings offer (like when you use control or escape sequences).您通常会发现这并不令人满意,因为您没有获得现代终端设置提供的任何功能(例如当您使用控制或转义序列时)。 For example, notice that you might have trouble exiting the program with ctrl-C .例如,请注意您可能无法使用ctrl-C退出程序。 Consequently you should put the terminal back into cooked mode once you are done reading input characters.因此,您应该在读取完输入字符后将终端重新置于熟化模式 The termios.tcsetattr call simply says "put the terminal back the way I found it". termios.tcsetattr调用只是说“将终端放回我找到它的方式”。 It knows how to do this by first calling termios.tcgetattr at the beginning of the program which is saying "tell me all the current settings for the terminal".它知道如何通过首先在程序开头调用termios.tcgetattr来做到这一点,它说“告诉我终端的所有当前设置”。

Once you understand all that, you should easily be able to encapsulate the functionality in a function that suits your program.一旦理解了所有这些,您应该能够轻松地将功能封装在适合您的程序的函数中。

1 stdin is the stream that input comes to you from the user. 1 stdin是用户输入的流。 Wikipedia can tell you more about standard streams . Wikipedia 可以告诉您更多关于标准流的信息。

Using a good lightweight module curtsies you could do something like this (taken from their examples/ directory):使用一个好的轻量级模块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()

So pressing keys on your keyboard gives you something like this:所以按下键盘上的键会给你这样的东西:

'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'

curtsies is used by bpython as a low level abstraction of terminal-related stuff. bpython 使用curtsies作为终端相关内容的低级抽象。

The basic problem of decoding the input is that in different terminals and terminal emulator programs like xterm or gnome-terminals physically same keys produce different keycode sequences.解码输入的基本问题是,在不同的终端和终端仿真程序(如xtermgnome-terminals物理上相同的键会产生不同的键码序列。 That's why one needs to know which terminal settings should be used to decode input.这就是为什么需要知道应该使用哪些终端设置来解码输入的原因。 Such a module helps to abstract from those gory details.这样的模块有助于从那些血淋淋的细节中抽象出来。

Since your question states that you are using a Raspberry Pi and a USB HID keyboard peripheral, but does not specify whether or not you have the Pi configured to boot into text or graphical mode where you will be running your script, I would suggest using libinput which will work in either case.由于您的问题表明您正在使用 Raspberry Pi 和 USB HID 键盘外围设备,但没有指定您是否将 Pi 配置为启动到运行脚本的文本或图形模式,我建议使用 libinput在任何一种情况下都可以使用。

You can use libinput's python bindings to read keyboard (and most other input devices) events directly from the kernel.您可以使用 libinput 的 python 绑定直接从内核读取键盘(和大多数其他输入设备)事件。

pip3 install python-libinput

The interface to this subsystem is exposed through the character devices which usually live in /dev/input/.该子系统的接口通过通常位于 /dev/input/ 中的字符设备公开。 They are managed by udev rules which create one or more character devices per attached input device, and are added and removed dynamically when, for example, a USB keyboard is attached or unplugged, or a Bluetooth mouse connects or disconnects.它们由 udev 规则管理,这些规则为每个连接的输入设备创建一个或多个字符设备,并在例如连接或拔下 USB 键盘,或连接或断开蓝牙鼠标时动态添加和删除。

Libinput handles for you the task of opening and reading from all attached input devices, and opening and closing devices when they are added and removed, respectively. Libinput 为您处理打开和读取所有连接的输入设备的任务,以及分别在添加和移除设备时打开和关闭设备。

Using libinput from python to read key events would look like this:使用 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")

One minor gotcha to be aware of is that udev is commonly configured to set permissions on the event devices it creates in /dev/input/ to allow access only from users who are members of a special supplementary 'input' group, since to allow unrestricted access to raw user key and mouse input would be a major security flaw.需要注意的一个小问题是,udev 通常配置为在 /dev/input/ 中创建的事件设备上设置权限,以仅允许属于特殊补充“输入”组成员的用户访问,因为允许不受限制访问原始用户键和鼠标输入将是一个主要的安全漏洞。 As such, o if running this throws an error during the libinput initialisation, you may need to add input to your user's supplementary groups by running:因此,如果在 libinput 初始化期间运行它会引发错误,您可能需要通过运行以下命令将输入添加到用户的补充组:

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

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

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