raw_input without pressing enter

Viewed 109402

I'm using raw_input in Python to interact with user in shell.

c = raw_input('Press s or n to continue:')
if c.upper() == 'S':
    print 'YES'

It works as intended, but the user has to press enter in the shell after pressing 's'. Is there a way to accomplish what I need from an user input without needing to press enter in the shell? I'm using *nixes machines.

10 Answers

Actually in the meantime (almost 10 years from the start of this thread) a cross-platform module named pynput appeared. Below a first cut - i.e. that works with lowercase 's' only. I have tested it on Windows but I am almost 100% positive that it should work on Linux.

from pynput import keyboard

print('Press s or n to continue:')

with keyboard.Events() as events:
    # Block for as much as possible
    event = events.get(1e6)
    if event.key == keyboard.KeyCode.from_char('s'):
        print("YES")

curses can do that as well :

import curses, time

def input_char(message):
    try:
        win = curses.initscr()
        win.addstr(0, 0, message)
        while True: 
            ch = win.getch()
            if ch in range(32, 127): 
                break
            time.sleep(0.05)
    finally:
        curses.endwin()
    return chr(ch)

c = input_char('Do you want to continue? y/[n]')
if c.lower() in ['y', 'yes']:
    print('yes')
else:
    print('no (got {})'.format(c))

If you can use an external library, blessed (cross platform) can do do this quite easily:

from blessed import Terminal

term = Terminal()

with term.cbreak(): # set keys to be read immediately 
    print("Press any key to continue")
    inp = term.inkey() # wait and read one character

Note that while inside the with block, line editing capabilities of the terminal will be disabled.

Documentation for cbreak, inkey, and an example with inkey.

I know this is old, but the solution wasn't good enough for me. I need the solution to support cross-platform and without installing any external Python packages.

My solution for this, in case anyone else comes across this post

Reference: https://github.com/unfor19/mg-tools/blob/master/mgtools/get_key_pressed.py

from tkinter import Tk, Frame


def __set_key(e, root):
    """
    e - event with attribute 'char', the released key
    """
    global key_pressed
    if e.char:
        key_pressed = e.char
        root.destroy()


def get_key(msg="Press any key ...", time_to_sleep=3):
    """
    msg - set to empty string if you don't want to print anything
    time_to_sleep - default 3 seconds
    """
    global key_pressed
    if msg:
        print(msg)
    key_pressed = None
    root = Tk()
    root.overrideredirect(True)
    frame = Frame(root, width=0, height=0)
    frame.bind("<KeyRelease>", lambda f: __set_key(f, root))
    frame.pack()
    root.focus_set()
    frame.focus_set()
    frame.focus_force()  # doesn't work in a while loop without it
    root.after(time_to_sleep * 1000, func=root.destroy)
    root.mainloop()
    root = None  # just in case
    return key_pressed


def __main():
        c = None
        while not c:
                c = get_key("Choose your weapon ... ", 2)
        print(c)

if __name__ == "__main__":
    __main()

Standard library solution for Unix-like operating systems (including Linux):

def getch():
    import sys, termios

    fd = sys.stdin.fileno()
    orig = termios.tcgetattr(fd)

    new = termios.tcgetattr(fd)
    new[3] = new[3] & ~termios.ICANON
    new[6][termios.VMIN] = 1
    new[6][termios.VTIME] = 0

    try:
        termios.tcsetattr(fd, termios.TCSAFLUSH, new)
        return sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, orig)

This works by putting the terminal into non-canonical input mode before reading from the terminal.

Alternative solution that does not echo the user's input (e.g. if the user presses z, z will not appear on screen):

def getch():
    import sys, termios, tty

    fd = sys.stdin.fileno()
    orig = termios.tcgetattr(fd)

    try:
        tty.setcbreak(fd)  # or tty.setraw(fd) if you prefer raw mode's behavior.
        return sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, orig)

Usage example:

print('Press s or n to continue: ', end='', flush=True)
c = getch()
print()
if c.upper() == 'S':
    print('YES')
Related