使用 curses 立即检测用户输入

Use curses to detect user input immediately

我在 python2.7 中使用诅咒来控制机器人。我想用例如引导它w 键,告诉它应该前进。这是我的代码,去掉了所有机器人控制:

import curses
from time import sleep

if __name__ == "__main__":
    shell = curses.initscr()
    shell.nodelay(True)

    while True:
        key = shell.getch()

        if key == 119:
            print("key w pressed")

        sleep(0.03)

这很好用,只是我必须按 enter 键才能识别密钥。所以我可以多次按下 w,当我按下 enter 时,机器人会完全按照它应该做的去做,或者在这个例子中,文本 key w pressed 出现的次数与我按下它的次数一样多。但我希望这会立即发生,即不必按回车键。如何实现?

curses.cbreak() 添加到您的设置中,并在清理时调用 curses.nocbreak() 将终端恢复到可用状态。

您可以通过捕获 ctrl-c 作为异常来进行清理。例如:

 try:
     curses.cbreak()
     while True:
            key = shell.getch()
            if key == 119:
                print("key w pressed")
            sleep(0.03)
 except KeyboardInterrupt:
     curses.nocbreak()

鉴于 OP 没有使用 window.keypad, calling either curses.raw or curses.cbreak 将给出(几乎)相同的结果以允许脚本读取未缓冲的字符。

调用curses.endwin() is a more reliable way to restore things. Bear in mind that it is a wrapper around curses. curses simulates cbreak mode, setting the real terminal mode to raw. Calling curses.nocbreak对真实终端模式影响。

如果你调用 nocbreak 来清理而不是调用 endwin,你将有额外的问题需要解决(参见 Clean up ncurses mess in terminal after a crash 例如),因为终端将处于原始模式(包括真正的 noecho,这使得打字 stty sane^M 很痛苦)。

参考 endwin

的文档

A program should always call endwin before exiting or escaping from curses mode temporarily. This routine

   o   restores tty modes,

   o   moves the cursor to the lower left-hand corner of  the
       screen and

   o   resets the terminal into the proper non-visual mode. 

如前所述,cbreakraw 几乎 相同。主要区别在于程序如何响应键盘中断(如 ^C)。如果您碰巧正在使用 ncurses,它会建立 signal handlers,它(对于 SIGINTSIGTERM)将调用 curses.endwin。其他 curses 实现不会这样做,即使您的脚本调用 curses.endwin 也不确定它是否会按预期工作(因为 curses 倾向于调用不安全的流 I/O 函数,这些函数不能在信号处理程序)。

现有答案正确,cbreak() 将关闭缓冲输入模式,这意味着 getch() 将 return 立即按下一个键。

我建议使用 curses.wrapper() 函数。这将设置屏幕和缓冲,在这种情况下如何。但它也会捕获异常并确保终端在您的应用程序退出时 return 进入干净状态。了解一下 here

基于 OP 代码的示例是:

import curses
from time import sleep

def program_loop(stdscr):
    while True:
        key = stdscr.getch()


        if key == 119:
            print("key w pressed\r")

        sleep(0.03)

if __name__ == "__main__":
    curses.wrapper(program_loop)