ChatGPT解决这个技术问题 Extra ChatGPT

How to make a python script wait for a pressed key?

How do I make my python script wait until the user presses any key?


M
Mateen Ulhaq

In Python 3, use input():

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

In Python 2, use raw_input():

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

This only waits for the user to press enter though.

On Windows/DOS, one might want to use msvcrt. The msvcrt module gives you access to a number of functions in the Microsoft Visual C/C++ Runtime Library (MSVCRT):

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

This should wait for a key press.

Notes:

In Python 3, raw_input() does not exist.
In Python 2, input(prompt) is equivalent to eval(raw_input(prompt)).


I'm getting this error when I try to do this in Python 2.7: "SyntaxError: unexpected EOF while parsing"
@Solarsaturn9 and an increasing and large number do not. Thus this answer did not work for me, and the many other that come here.
@richard using input() should work on other platforms as well. It's ridiculous to dock points for providing a alternative Windows only solution when the first solution is multi-platform.
@Solarsaturn9 read the question and answer again: input does not continue if any key is pressed, only if enter is pressed.
@JonTirsen that's because Python 2.7 has a function called input which evaluates the string you input. To fix, use raw_input
M
Mateen Ulhaq

In Python 3, use input():

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

In Python 2, use raw_input():

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

What about when it can be one of a number of keys? Not just enter?
Using six for Py2 & Py3 compatible code: from six.moves import input; input("Press Enter to continue...")
m
mheyman

On my linux box, I use the following code. This is similar to code I've seen elsewhere (in the old python FAQs for instance) but that code spins in a tight loop where this code doesn't and there are lots of odd corner cases that code doesn't account for that this code does.

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)

While this is my favorite of the answers here, like the others doesn't catch things like shift, control, etc
@Mala that pretty much isn't possible in pure Python; perhaps you should write a C module?
ctrl-c is an ascii 3 so that is expected. If you want to raise a signal on ctrl-c, the easy solution is to put an if ord(returned_value) == 3: os.kill(os.getpid(), signal.SIGINT) but you could also turn off signal processing by attrs[0] |= termios.BRKINT, attrs[3] != termios.ISIG, and get rid of the except KeyboardInterrupt processing. Note - I changed the return value for KeyboardInterrupt into a '\x03' in honor of your query (and because that makes this code always return a string).
How could the above code be adjusted so that it returns a tuple for a complex key press like "Page Up" or "Left Arrow"?
I updated the code in your honor, Derek. That was a good question. I bounced back an forth between returning a string to returning a tuple as in your suggestion and ended with the tuple because it makes other code using the results clearer (in my opinion). Oh, the answer was to turn on non-blocking after the first character and read any additional characters. If you type real fast you can fool this...
C
CrouZ

If you are ok with depending on system commands you can use the following:

Linux and Mac OS X:

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

Windows:

import os
os.system("pause")

If you want keep running until a signal is raised (like SIGINT), you could also check the return value from system and then call sys.exit(0).
Is there an equivalent for OSX?
@CarlosE The Linux variant might work for OSX too. Please let me know if it does.
@CarlosE I tested on MAC OS X 10.7 and there it works with both Python 3.7.6 and Python 2.7.1. How are you running it? According to this question, you need to run it from a terminal: stackoverflow.com/questions/58986403/…
@Mehdi: Looks like the default shell (sh) does not support my read command. I have edited my answer so that bash is always used. Does this solve the issue for you?
A
Ahmed Akhtar

Simply using

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

will cause the following error when using Python 2:

SyntaxError: expected EOF while parsing.

Simple fix for the code to work on both Python 2 and Python 3 is to use:

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

Don't use input in python 2 - the correct function is raw_input. In python 2, input is equivalent to eval(raw_input()).
This ignores all keys the user presses, until they hit enter, which is quite different from what the OP is asking.
Also, if you were going to use 'input', catching a SyntaxError isn't appropriate. Whatever the user types gets evaluated, so if, for example, they type "1/0" then a ZeroDivisionError is raised instead of a SyntaxError, and your program will exit.
As @Blorgbeard mentioned, simply using raw_input("Press Enter to continue...") will suffice. I use it often now when debugging.
G
Gringo Suave

Cross Platform, Python 2/3 code:

# 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

I removed the fctl/non-blocking stuff because it was giving IOErrors and I didn't need it. I'm using this code specifically because I want it to block. ;)

Addendum:

I implemented this in a package on PyPI with a lot of other goodies called console:

>>> from console.utils import wait_key

>>> wait_key()
'h'

@Benoit which OS?
Linux - Ubuntu 20.04
Works for me, are you piping it?
R
Richard

The python manual provides the following:

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)

which can be rolled into your use case.


It's good practice to copy the thing you're linking to so that the knowledge remains, even if the link dies (and they do!).
How can I make this work in Python 3.x? In 3.x, after changing the print statement to be compatible, this just loops infinitely and doesn't wait for input. It works great in Python 2, though.
The link has been updated to redirect to a different page. The new link is here.
in python3 this works after rewriting the print statement in a slightly more than 2to3 way: if c: print(f"Got character {repr(c)}"), as of now, the python 3 faq is just blank on this.
J
John Gaines Jr.

I don't know of a platform independent way of doing it, but under Windows, if you use the msvcrt module, you can use its getch function:

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

mscvcrt also includes the non-blocking kbhit() function to see if a key was pressed without waiting (not sure if there's a corresponding curses function). Under UNIX, there is the curses package, but not sure if you can use it without using it for all of the screen output. This code works under UNIX:

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

Note that curses.getch() returns the ordinal of the key pressed so to make it have the same output I had to cast it.


Using curses is a lot nicer than the rather convoluted examples described by the manual, even if it involves a huge dependency. +1
r
ralfiii

I am new to python and I was already thinking I am too stupid to reproduce the simplest suggestions made here. It turns out, there's a pitfall one should know:

When a python-script is executed from IDLE, some IO-commands seem to behave completely different (as there is actually no terminal window).

Eg. msvcrt.getch is non-blocking and always returns $ff. This has already been reported long ago (see e.g. https://bugs.python.org/issue9290 ) - and it's marked as fixed, somehow the problem seems to persist in current versions of python/IDLE.

So if any of the code posted above doesn't work for you, try running the script manually, and NOT from IDLE.


a
andrew pate

If you want to wait for enter (so the user knocking the keyboard does not cause something un-intended to happen) use

sys.stdin.readline()

The whole point is for the user to not have to press only the Enter key, to for example be able to just slap the spacebar. If you require Enter to avoid something unintended from happening, then that's bad design.
w
w4dd325

Could use the 'Keyboard' library...

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

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

In Linux, this requires root permissions.
M
Maxime Lorant

os.system seems to always invoke sh, which does not recognize the s and n options for read. However the read command can be passed to bash:

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

The read documentation makes me think it will not timeout unless you specify the -t option.
g
ghost21blade

You can put a while loop and listen for your desired key press event.

Example :

import keyboard

while True:
    if keyboard.is_pressed('shift') :
        break

print('Shift is pressed')

A
Achal Dave

If you want to see if they pressed a exact key (like say 'b') Do this:

while True:
    choice = raw_input("> ")

    if choice == 'b' :
        print "You win"
        input("yay")
        break

This requires the user to type 'b' (or something else) then press enter, which is quite different from what the OP is asking for.