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

jv4diomz  于 2022-11-22  发布在  Linux
关注(0)|答案(3)|浏览(265)

现在我正在尝试用raspberry pi和makey makey编写一个小代码。makey makey是一个小的电路板,当某些触点通电时,它就像一个usb键盘。我的问题是,在python脚本中检测这些按键的最简单的方法是什么。我知道使用GPIO引脚会更容易,但是现在我正在寻找这个。我已经看到了一些例子,比如使用getch()来自msvcrt(据我所知,这是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")

等等。谢谢。

gjmwrych

gjmwrych1#

这是一个简单的循环,它将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上,程序的输入是line buffered。这意味着操作系统将缓冲输入直到它有一整行,所以程序甚至看不到用户键入的任何内容,直到用户也点击了'enter'。换句话说,如果程序期望用户键入'w',而用户却这样做了,'w'会一直放在操作系统的缓冲区中,直到使用者按下'enter'为止。此时整行会传送到程式,所以您会取得字串“w\n”做为使用者的输入。
你可以通过把tty设置为raw mode来禁用它。你可以用Python函数tty.setcbreak来实现这一点,它会调用linux中的tty驱动程序来告诉它停止缓冲。我传递了sys.stdin参数来告诉它我想为1关闭哪个流的缓冲。所以在tty.setcbreak调用之后,上面的循环将为用户按下的每个键给予输出。
然而,一个复杂的问题是,一旦程序退出,tty仍然处于原始模式。(就像使用控制序列或转义序列时一样)。例如,请注意,使用ctrl-C退出程序时可能会遇到问题。因此,您应该将终端重新设置为cooked模式一旦你读完了输入字符。termios.tcsetattr调用只是说“把终端放回我找到它的地方”。它知道如何做到这一点,首先在程序开始时调用termios.tcgetattr,这是说“告诉我终端的所有当前设置”。
一旦你理解了所有这些,你应该很容易就能把这些功能封装到一个适合你的程序的函数中。
1 stdin是用户输入给你的流。维基百科可以告诉你更多关于standard streams的信息。

muk1a3rh

muk1a3rh2#

使用一个好的轻量级模块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)中,物理上相同的键产生不同的键码序列。这就是为什么我们需要知道应该使用哪些终端设置来解码输入。这样的模块有助于从那些血淋淋的细节中抽象出来。

dvtswwa3

dvtswwa33#

由于您的问题指出您使用的是Raspberry Pi和USB HID键盘外设,但没有指定您是否将Pi配置为 Boot 到文本或图形模式,您将在其中运行脚本,我建议使用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/中创建的事件设备上设置权限,以仅允许属于特殊补充“input”组的用户访问,因为允许不受限制地访问原始用户键和鼠标输入将是一个重大的安全缺陷。因此,o如果运行此命令会在libinput初始化期间抛出错误,您可能需要通过运行以下命令将输入添加到用户的补充组:

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

相关问题