从用户处读取击键

2024-09-27 07:24:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我想从用户那里读一个:字母、数字和类似ESCDEL和箭头键的东西

到目前为止,我一直在使用名为readchar的第三方模块。这里讨论了执行该任务的几种方法:How to read a single character from the user?。它们沿着以下路线运行:

import termios, sys, tty
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
        tty.setraw(fd)
        ch = sys.stdin.read(1)
finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

不幸的是,这只返回单个字符,如\x1b,而箭头将返回多个字符,如\x1b\x5b\x41。模块readchar试图通过提供一个函数readkey来解决这个问题,该函数读取字符,直到is读取了一个完整的键,然后返回这个值。问题是,当按下ESC键时,它将挂起,因为它预期会有更多字符

我如何编写一个函数来立即返回被按下的键,无论它是ESC还是DEL还是其他什么


Tags: 模块函数readsettingsstdinsys字符old
3条回答

如果在终端中执行此操作,则需要在代码中使用自定义逻辑来处理它

背景:据您的程序所知,它可能连接到一个传输速率为每秒300位的老式硬件终端。当按下向上箭头时,该终端可能有一个向上箭头键自动发送字符^[[A,或者它甚至可能没有向上箭头,在这种情况下,用户必须按顺序按这三个组合键以获得向上箭头行为(^+[esc)都发送命令^[字符)。或者,当按下向上箭头时,它可能是发送不同字符序列的不同型号的终端。如果在Python中使用input(),操作系统将负责从stdin读取,识别远程终端(或用户终端应用程序模拟的终端),并在命令行上将这些序列转换为适当的编辑操作。但是,如果您直接从stdin读取,则每当用户按下特殊键时,您将获得一些任意字符序列(这可能会因他们模拟的终端型号而异)

在我的终端中,向上箭头发送字符序列^[{}{}(您可以通过运行cat,然后按键并查看显示的内容来检查)。因此,如果您正在逐个字符地读取终端,则无法判断用户是否刚刚按下了esc,或者他们是否按下了发出字符序列的其他键

假设您有一个很好的方法将字符序列转换回击键,处理这个问题的一种方法是读取sys.stdin上当前可用的所有内容(使用sys.stdin.read()而不是sys.stdin.read(1)),然后处理缓冲区中的所有内容。有时这会起作用,因为终端会同时将所有字符推入缓冲区,而你的应用程序会同时获取所有字符。但这不是100%保证的read原则上,你可以用任何一种感觉来分解流。通过缓慢的终端连接,你的应用程序可能有时间在下一个待处理字符出现之前处理每个字符

你可以在没有新输入的情况下等待一段安静的时间,然后处理你目前拥有的一切。但是安静时间的长短将取决于连接速度、处理器负载等,所以这是不确定的。而且,延迟越长,你的程序越可靠,但也越滞后

在某种程度上,您正在尝试重新定义什么是程序的输入。因此,也许您应该一直这样做?我的意思是,命令行应用程序通常不会自行响应esc字符;相反,它们会接受它并等待接下来的任何操作。因此,用户可以键入esc[[A尽可能慢,应用程序将依次处理每一个,并最终确定它有一个完整的序列,然后执行向上箭头。如果你的应用程序应该响应esc,那么也许你应该这样做,记住大多数人的终端都会发送^{当他们按下向上箭头时,后面跟着一堆附加字符。因此,如果其他内容不重要,您可以扔掉它们,或者等待,然后像其他终端程序一样对整个序列执行操作

另一种选择可能是使用Python ^{}库更直接地与终端交互,但我对此不太熟悉

我认为这种情况的总结版本是:每当用户按下esc或其他各种终端特定键时,stdin不可避免地会给您一个^[字符。区分按下哪个键的唯一方法是开始检查^[之后的其他字符,可能会有短暂的延迟每个字符(可能是0.1s,使用select.select)。如果您获得了与特定键对应的字符序列,则可以使用该序列。如果有足够长的延迟且没有额外的字符,那么您将放弃并处理到目前为止所拥有的内容(可能只是^[,可能是部分转义序列)。如果这还不够好,那么您需要使用一个与本地硬件更直接交互的库

curses库provides these behaviors-超时,汇编序列,使用terminfo数据库转换为通用击键。可能有一些更轻的解决方案,但我还没有看到

只是为了完成讨论:一些终端允许您向控制台打印转义序列,该序列将重新编程功能键,包括箭头键,以发送不同的字符或字符序列。e、 g.有一个用于VT100终端和模拟器的DECPFK命令。这可能比使用curses更简单(只需将箭头键编程为发送<>或除^[以外的任何内容)。但是,序列可能因终端而异,因此您需要在terminfo数据库中查找它(如果有的话)。我怀疑这是否值得费心

其中一些可能提供有用的背景:

这个keyboard库似乎做了您想要做的事情,并且工作正常​在所有主要的操作系统上。只需使用keyboard.read_key()

sshkeyboard呢?它知道如何处理最常见的多字符键,如箭头

只需使用pip install sshkeyboard安装

然后编写脚本,例如:

from sshkeyboard import listen_keyboard

def press(key):
    print(f"'{key}' pressed")

def release(key):
    print(f"'{key}' released")

listen_keyboard(
    on_press=press,
    on_release=release,
)

它将打印:

'left' pressed
'left' released

按下向左箭头键时^默认情况下,{}键结束侦听

相关问题 更多 >

    热门问题