【问题标题】:How to make a python script wait for a pressed key?如何让python脚本等待按下的键?
【发布时间】:2010-11-02 06:31:53
【问题描述】:

我希望我的 python 脚本等到用户按下任意键。
我该怎么做?

【问题讨论】:

    标签: python wait keyboard-input


    【解决方案1】:

    Python 3 中使用 input():

    input("Press Enter to continue...")
    

    Python 2 中使用raw_input()

    raw_input("Press Enter to continue...")
    

    这只是等待用户按下回车键。

    可能想要使用 msvcrt((仅限 Windows/DOS)msvcrt 模块让您可以访问 Microsoft Visual C/C++ 运行时库中的许多函数(MSVCRT)):

    import msvcrt as m
    def wait():
        m.getch()
    

    这应该等待按键。

    附加信息:

    在 Python 3 中 raw_input() 不存在

    在 Python 2 中,input(prompt) 等价于 eval(raw_input(prompt))

    【讨论】:

    • 当我尝试在 Python 2.7 中执行此操作时遇到此错误:“SyntaxError: unexpected EOF while parsing”
    • @Solarsaturn9 和越来越多的数字没有。因此,这个答案对我和其他许多人都不起作用。
    • @richard 使用 input() 应该也可以在其他平台上工作。当第一个解决方案是多平台时,为提供仅 Windows 的替代解决方案而停靠点是荒谬的。
    • @Solarsaturn9 阅读问题并再次回答:input 不会继续按下任何键,只有按下回车。
    • @JonTirsen 这是因为 Python 2.7 有一个名为 input 的函数,它可以评估您输入的字符串。要修复,请使用 raw_input
    【解决方案2】:

    在 Python 2 中执行此操作的一种方法是使用 raw_input():

    raw_input("Press Enter to continue...")
    

    在 python3 中它只是input()

    【讨论】:

    • 什么时候它可以是多个键之一?不仅仅是enter
    • With Python 3+,这已更改为 input()
    • six 用于Py2 和Py3 兼容代码:from six.moves import input; input("Press Enter to continue...")
    【解决方案3】:

    在我的 linux 机器上,我使用以下代码。这类似于我在其他地方看到的代码(例如在旧的 python 常见问题解答中),但是代码在一个紧密的循环中旋转,而这段代码没有,并且有很多奇怪的极端情况,代码没有考虑到这一点代码可以。

    def read_single_keypress():
        """Waits for a single keypress on stdin.
    
        This is a silly function to call if you need to do it a lot because it has
        to store stdin's current setup, setup stdin for reading single keystrokes
        then read the single keystroke then revert stdin back after reading the
        keystroke.
    
        Returns a tuple of characters of the key that was pressed - on Linux, 
        pressing keys like up arrow results in a sequence of characters. Returns 
        ('\x03',) on KeyboardInterrupt which can happen when a signal gets
        handled.
    
        """
        import termios, fcntl, sys, os
        fd = sys.stdin.fileno()
        # save old state
        flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
        attrs_save = termios.tcgetattr(fd)
        # make raw - the way to do this comes from the termios(3) man page.
        attrs = list(attrs_save) # copy the stored version to update
        # iflag
        attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                      | termios.ISTRIP | termios.INLCR | termios. IGNCR
                      | termios.ICRNL | termios.IXON )
        # oflag
        attrs[1] &= ~termios.OPOST
        # cflag
        attrs[2] &= ~(termios.CSIZE | termios. PARENB)
        attrs[2] |= termios.CS8
        # lflag
        attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                      | termios.ISIG | termios.IEXTEN)
        termios.tcsetattr(fd, termios.TCSANOW, attrs)
        # turn off non-blocking
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
        # read a single keystroke
        ret = []
        try:
            ret.append(sys.stdin.read(1)) # returns a single character
            fcntl.fcntl(fd, fcntl.F_SETFL, flags_save | os.O_NONBLOCK)
            c = sys.stdin.read(1) # returns a single character
            while len(c) > 0:
                ret.append(c)
                c = sys.stdin.read(1)
        except KeyboardInterrupt:
            ret.append('\x03')
        finally:
            # restore old state
            termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
            fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
        return tuple(ret)
    

    【讨论】:

    • 虽然这是我最喜欢的答案,但就像其他人无法捕捉到转变、控制等内容一样
    • @Mala 这在纯 Python 中几乎是不可能的;也许你应该写一个 C 模块?
    • ctrl-c 是 ascii 3,所以这是意料之中的。如果您想在 ctrl-c 上发出信号,简单的解决方案是放置 if ord(returned_value) == 3: os.kill(os.getpid(), signal.SIGINT) 但您也可以关闭信号处理通过 attrs[0] |= termios.BRKINT, attrs[3] != termios.ISIG,去掉除了 KeyboardInterrupt 处理。注意 - 我将 KeyboardInterrupt 的返回值更改为 '\x03' 以纪念您的查询(因为这使得此代码总是返回一个字符串)。
    • 如何调整上面的代码,使其返回一个元组,用于像“Page Up”或“Left Arrow”这样的复杂按键?
    • 我更新了代码以纪念你,Derek。这是个好问题。我在返回字符串和返回元组之间来回反弹,如您的建议并以元组结束,因为它使使用结果的其他代码更清晰(在我看来)。哦,答案是在第一个字符之后打开非阻塞并读取任何其他字符。如果你打字真快,你可以骗过这个......
    【解决方案4】:

    如果您可以根据系统命令使用以下命令:

    Linux 和 Mac OS X:

    import os
    os.system('read -s -n 1 -p "Press any key to continue..."')
    print()
    

    窗户:

    import os
    os.system("pause")
    

    【讨论】:

    • 如果你想一直运行直到一个信号产生(比如 SIGINT),你也可以检查system 的返回值,然后调用sys.exit(0)
    • OSX 有没有等价物?
    • @CarlosE Linux 变体可能也适用于 OSX。如果有请告诉我。
    • 不幸的是,Linux 变体在 OSX 中没有 - 声明无效。我正在运行 Big Sur (11.1)。
    • @CarlosE 我在 MAC OS X 10.7 上进行了测试,它适用于 Python 3.7.6 和 Python 2.7.1。你是如何运行它的?根据这个问题,您需要从终端运行它:stackoverflow.com/questions/58986403/…
    【解决方案5】:

    简单地使用

    input("Press Enter to continue...")
    

    在使用 Python 2 时会出现以下错误:

    SyntaxError: 解析时预期 EOF。

    在 Python 2 和 Python 3 上工作的代码的简单修复是使用:

    try:
        input("Press enter to continue")
    except SyntaxError:
        pass
    

    【讨论】:

    • 不要在 python 2 中使用input - 正确的函数是raw_input。在 python 2 中,input 等价于eval(raw_input())
    • 这会忽略用户按下的所有键,直到他们按下回车键,这与 OP 的要求完全不同。
    • 另外,如果你打算使用'input',捕获一个 SyntaxError 是不合适的。无论用户类型如何被评估,例如,如果他们输入“1/0”,则会引发 ZeroDivisionError 而不是 SyntaxError,并且您的程序将退出。
    • 正如@Blorgbeard 提到的,只需使用 raw_input("Press Enter to continue...") 就足够了。我现在调试的时候经常使用它。
    【解决方案6】:

    跨平台,Python 2/3 代码:

    # import sys, os
    
    def wait_key():
        ''' Wait for a key press on the console and return it. '''
        result = None
        if os.name == 'nt':
            import msvcrt
            result = msvcrt.getch()
        else:
            import termios
            fd = sys.stdin.fileno()
    
            oldterm = termios.tcgetattr(fd)
            newattr = termios.tcgetattr(fd)
            newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
            termios.tcsetattr(fd, termios.TCSANOW, newattr)
    
            try:
                result = sys.stdin.read(1)
            except IOError:
                pass
            finally:
                termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    
        return result
    

    我删除了 fctl/非阻塞的东西,因为它提供了 IOErrors 而我不需要它。我专门使用此代码是因为我希望它被阻止。 ;)

    附录:

    我在 PyPI 上的一个包中实现了这一点,还有很多其他的好东西,叫做 console

    >>> from console.utils import wait_key
    
    >>> wait_key()
    'h'
    

    【讨论】:

    • @Benoit 哪个操作系统?
    • Linux - Ubuntu 20.04
    • 为我工作,你用管道吗?
    【解决方案7】:

    pythonmanual 提供以下功能:

    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    
    oldterm = termios.tcgetattr(fd)
    newattr = termios.tcgetattr(fd)
    newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSANOW, newattr)
    
    oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
    
    try:
        while 1:
            try:
                c = sys.stdin.read(1)
                print "Got character", repr(c)
            except IOError: pass
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
        fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
    

    可以融入您的用例。

    【讨论】:

    • 复制您要链接的内容是一种很好的做法,这样知识就可以保留,即使链接死了(他们确实死了!)。
    • 如何在 Python 3.x 中完成这项工作?在 3.x 中,将 print 语句更改为兼容后,这只是无限循环并且不等待输入。不过,它在 Python 2 中运行良好。
    • 链接已更新为重定向到不同的页面。新链接是here.
    • 在 python3 中,这在以略高于 2to3 的方式重写 print 语句后起作用:if c: print(f"Got character {repr(c)}"),截至目前,the python 3 faq 只是空白。
    【解决方案8】:

    我不知道独立于平台的方法,但是在Windows下,如果你使用msvcrt模块,你可以使用它的getch函数:

    import msvcrt
    c = msvcrt.getch()
    print 'you entered', c
    

    mscvcrt 还包括非阻塞 kbhit() 函数,以查看是否在没有等待的情况下按下了键(不确定是否有相应的 curses 函数)。在 UNIX 下,有 curses 包,但不确定是否可以在不将其用于所有屏幕输出的情况下使用它。此代码在 UNIX 下工作:

    import curses
    stdscr = curses.initscr()
    c = stdscr.getch()
    print 'you entered', chr(c)
    curses.endwin()
    

    请注意,curses.getch() 返回按下的键的序号,以便使其具有与我必须强制转换的相同的输出。

    【讨论】:

    • 使用 curses 比手册中描述的相当复杂的示例要好得多,即使它涉及巨大的依赖关系。 +1
    【解决方案9】:

    我是 python 新手,我已经认为我太愚蠢了,无法重现这里提出的最简单的建议。 事实证明,有一个陷阱应该知道:

    当从 IDLE 执行 python 脚本时,一些 IO 命令的行为似乎完全不同(因为实际上没有终端窗口)。

    例如。 msvcrt.getch 是非阻塞的并且总是返回 $ff。 这已经在很久以前报告过了(参见例如https://bugs.python.org/issue9290) - 它被标记为已修复,不知何故,问题似乎在当前版本的 python/IDLE 中仍然存在。

    因此,如果上面发布的任何代码都不适合您,请尝试手动运行脚本,并且不要从 IDLE 中运行

    【讨论】:

      【解决方案10】:

      如果你想等待输入(所以用户敲击键盘不会导致意外发生)使用

      sys.stdin.readline()
      

      【讨论】:

      • 重点是让用户不必只按 Enter 键,例如只需敲击空格键即可。如果您需要 Enter 来避免意外发生,那么这是糟糕的设计。
      【解决方案11】:

      os.system 似乎总是调用 sh,它不识别用于读取的 s 和 n 选项。但是可以将读取命令传递给 bash:

       os.system("""bash -c 'read -s -n 1 -p "Press any key to continue..."'""")
      

      【讨论】:

      • 阅读文档让我觉得它不会超时,除非你指定 -t 选项。
      【解决方案12】:

      可以使用“键盘”库...

      https://github.com/boppreh/keyboard#api

      import keyboard
      keyboard.wait('space')
      print('space was pressed, continuing...')
      

      【讨论】:

        【解决方案13】:

        如果您想查看他们是否按下了确切的键(例如说“b”),请执行以下操作:

        while True:
            choice = raw_input("> ")
        
            if choice == 'b' :
                print "You win"
                input("yay")
                break
        

        【讨论】:

        • 这要求用户输入“b”(或其他内容)然后按回车,这与 OP 要求的完全不同。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多