How do I wait for a pressed key?
Asked Answered
L

13

798

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

Leucocyte answered 11/6, 2009 at 20:9 Comment(0)
E
822

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)).

Easel answered 16/7, 2009 at 1:48 Comment(8)
I'm getting this error when I try to do this in Python 2.7: "SyntaxError: unexpected EOF while parsing"Bowfin
@Solarsaturn9 and an increasing and large number do not. Thus this answer did not work for me, and the many other that come here.Kirbykirch
@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.Roughhew
@Solarsaturn9 read the question and answer again: input does not continue if any key is pressed, only if enter is pressed.Kirbykirch
@JonTirsen that's because Python 2.7 has a function called input which evaluates the string you input. To fix, use raw_inputIslas
For people using IDLE: msvcrt.getch() doesn't work there, only in the Python console.Odont
Using six for Py2 and Py3 compatible code: from six.moves import input; input("Press Enter to continue...")Cardcarrying
six always waits for ENTER, not for any key.Ergocalciferol
M
342

In Python 3, use input():

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

In Python 2, use raw_input():

raw_input("Press Enter to continue...")
Modla answered 11/6, 2009 at 20:11 Comment(2)
What about when it can be one of a number of keys? Not just enter?Assemble
Using six for Py2 & Py3 compatible code: from six.moves import input; input("Press Enter to continue...")Cardcarrying
S
62

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)
Selfabsorbed answered 6/7, 2011 at 15:58 Comment(7)
While this is my favorite of the answers here, like the others doesn't catch things like shift, control, etcPhthalocyanine
@Phthalocyanine that pretty much isn't possible in pure Python; perhaps you should write a C module?Arciform
I'm getting "\x03" on keyboard interrupt (Ctrl-C) on my system.Amand
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).Selfabsorbed
How could the above code be adjusted so that it returns a tuple for a complex key press like "Page Up" or "Left Arrow"?Suppurate
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...Selfabsorbed
I liked this solution a lot. I've put up a gist with some utility I found useful in my case - a start at combining control sequences into one string and optional repeat filtering. gist.github.com/shaperilio/1b9065b149944d060a4da33d7bf6b78bClamatorial
S
48

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

from __future__ import print_function
import os
import platform

if platform.system() == "Windows":
    os.system("pause")
else:
    os.system("/bin/bash -c 'read -s -n 1 -p \"Press any key to continue...\"'")
    print()

It has been verified to work with Python 2 and 3 on Windows, Linux and Mac OS X.

Soph answered 5/6, 2013 at 6:36 Comment(9)
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).Economic
Is there an equivalent for OSX?Deuteronomy
@Deuteronomy The Linux variant might work for OSX too. Please let me know if it does.Soph
Unfortunately the Linux variant does not in OSX - the statement has no effect. Am running Big Sur (11.1).Deuteronomy
@Deuteronomy 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: #58986903Soph
Thank you @CrouZ. You are right, when using the command line this works. In Jupyter it does not work, however. Are you aware of a solution for Jupyter?Deuteronomy
@Deuteronomy Sorry, I have not used Jupyter so I don't know.Soph
sh: 1: read: Illegal option -sThirdrate
@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?Soph
D
42

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
Dniren answered 3/3, 2014 at 16:6 Comment(4)
Don't use input in python 2 - the correct function is raw_input. In python 2, input is equivalent to eval(raw_input()).Shadbush
This ignores all keys the user presses, until they hit enter, which is quite different from what the OP is asking.Lobule
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.Lobule
As @Shadbush mentioned, simply using raw_input("Press Enter to continue...") will suffice. I use it often now when debugging.Dniren
O
31

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.getwch()
    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'
Opah answered 22/1, 2016 at 22:10 Comment(5)
@Benoit which OS?Opah
Linux - Ubuntu 20.04Dulce
Works for me, are you piping it?Opah
On Windows your function returns a byte string.Hybrid
I see, think I'll use getwch() instead. #15Opah
B
19

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.

Bigham answered 15/2, 2011 at 13:9 Comment(4)
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!).According
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.Arciform
The link has been updated to redirect to a different page. The new link is here.Putrefy
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.Beauchamp
J
15

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.

Jasmin answered 17/5, 2011 at 21:42 Comment(1)
Using curses is a lot nicer than the rather convoluted examples described by the manual, even if it involves a huge dependency. +1Kassandrakassaraba
L
6

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.

Langsyne answered 28/7, 2016 at 21:47 Comment(0)
L
5

You could use the keyboard library:

import keyboard
keyboard.wait('space')
print('space was pressed, continuing...')
Livesay answered 22/9, 2021 at 19:21 Comment(2)
In Linux, this requires root permissions.Tussah
Root permission is required in MacOS as well.Ferricyanide
F
4

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()
Forespent answered 7/11, 2018 at 18:1 Comment(1)
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.Peignoir
G
3

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..."'""")
Geniagenial answered 18/12, 2013 at 6:39 Comment(1)
The read documentation makes me think it will not timeout unless you specify the -t option.Geniagenial
P
-1

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
Painterly answered 19/6, 2012 at 3:42 Comment(1)
This requires the user to type 'b' (or something else) then press enter, which is quite different from what the OP is asking for.Lobule

© 2022 - 2024 — McMap. All rights reserved.