【问题标题】:Detect arrow keys being pressed on console in Python在 Python 中检测控制台上按下的箭头键
【发布时间】:2018-12-03 10:57:56
【问题描述】:

我正在尝试用 Python 3 编写一个控制台程序,为用户提供某种 shell,就像控制台中的 Python 3 shell 一样。通过使用input()method,我能够相对快速地实现这一目标。但是,如果在那个 shell 中可以像在其他 shell 中一样使用箭头键循环浏览最近键入的命令,那就太好了。 input() 方法没有提供这个功能,我也没有找到任何其他简单的工具可以做到这一点,除了 curses 模块,它需要接管整个屏幕才能工作。我的一种方法是逐字节地从标准输入中读取输入的文本,然后根据我正在寻找的特殊字符的代码检查它。这工作得很好,但是当用户(出于某种原因)键入一个奇怪的 unicode 字符时,它会遇到问题,该字符包含一个键的代码,比如中间某处的箭头键。虽然这对我来说仍然是一个可以接受的解决方案,但考虑到它发生的频率,我觉得这个问题应该已经解决(更好)。

【问题讨论】:

  • 这是由控制台中的readline 库完成的,使用它。
  • @AChampion 检查箭头键按下?
  • readline 库已经做到了这一点并管理历史记录和行编辑,并且可以使用 .inputrc 进行配置。

标签: linux python-3.x io console-application


【解决方案1】:

在 Python 3 中,sys.stdin.read 将 unicode 字符作为单个字符返回。箭头键的转义序列作为多个 ASCII 字符提供。这是一个示例程序,使用ttytermios,它会相应地解析输入。

import sys,tty,termios

# Commands and escape codes
END_OF_TEXT = chr(3)  # CTRL+C (prints nothing)
END_OF_FILE = chr(4)  # CTRL+D (prints nothing)
CANCEL      = chr(24) # CTRL+X
ESCAPE      = chr(27) # Escape
CONTROL     = ESCAPE +'['

# Escape sequences for terminal keyboard navigation
ARROW_UP    = CONTROL+'A'
ARROW_DOWN  = CONTROL+'B'
ARROW_RIGHT = CONTROL+'C'
ARROW_LEFT  = CONTROL+'D'
KEY_END     = CONTROL+'F'
KEY_HOME    = CONTROL+'H'
PAGE_UP     = CONTROL+'5~'
PAGE_DOWN   = CONTROL+'6~'

# Escape sequences to match
commands = {
    ARROW_UP   :'up arrow',
    ARROW_DOWN :'down arrow',
    ARROW_RIGHT:'right arrow',
    ARROW_LEFT :'left arrow',
    KEY_END    :'end',
    KEY_HOME   :'home',
    PAGE_UP    :'page up',
    PAGE_DOWN  :'page down',
}

# Blocking read of one input character, detecting appropriate interrupts
def getch():
    k = sys.stdin.read(1)[0]
    if k in {END_OF_TEXT, END_OF_FILE, CANCEL}: raise KeyboardInterrupt
    print('raw input 0x%X'%ord(k),end='\r\n')
    return k

# Println for raw terminal mode
def println(*args):
    print(*args,end='\r\n',flush=True)

# Preserve current terminal settings (we will restore these before exiting)
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)

try:
    # Enter raw mode (key events sent directly as characters)
    tty.setraw(sys.stdin.fileno())

    # Loop, waiting for keyboard input
    while 1:
        # Parse known command escape sequences
        read = getch()
        while any(k.startswith(read) for k in commands.keys()): 
            if read in commands: 
                println('detected command (%s)'%commands[read])
                read = ''
                break
            read += getch()
        # Interpret all other inputs as text input
        for c in read:
            println('detected character 0x%X %c'%(ord(c),c))

# Always clean up
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 
    println('')
    sys.exit(0)

【讨论】:

    猜你喜欢
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2021-12-15
    • 1970-01-01
    • 2022-07-26
    相关资源
    最近更新 更多