raw_input without pressing enter
Asked Answered
V

11

44

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.

Virgo answered 19/8, 2010 at 15:7 Comment(2)
https://mcmap.net/q/49663/-polling-the-keyboard-detect-a-keypress-in-pythonPilpul
See this page. It uses the ttyl module and is only two lines. just omit the ord() command: #576150Sewole
H
24

Under Windows, you need the msvcrt module, specifically, it seems from the way you describe your problem, the function msvcrt.getch:

Read a keypress and return the resulting character. Nothing is echoed to the console. This call will block if a keypress is not already available, but will not wait for Enter to be pressed.

(etc -- see the docs I just pointed to). For Unix, see e.g. this recipe for a simple way to build a similar getch function (see also several alternatives &c in the comment thread of that recipe).

Hydro answered 19/8, 2010 at 15:24 Comment(3)
It works as intended, and having a cross-platform solution is great. Thanks for answering!Virgo
Go to pypi.python.org/pypi/readchar directly, seems to do most of this, although I can't get it to read arrow keys on OSX properly.Overdress
What about Linux and Mac users? Any alternative?Codpiece
C
25

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")
Commanding answered 11/4, 2020 at 21:22 Comment(1)
the problem with this is that it will detect key presses even when the python window is not selectedWealth
H
24

Under Windows, you need the msvcrt module, specifically, it seems from the way you describe your problem, the function msvcrt.getch:

Read a keypress and return the resulting character. Nothing is echoed to the console. This call will block if a keypress is not already available, but will not wait for Enter to be pressed.

(etc -- see the docs I just pointed to). For Unix, see e.g. this recipe for a simple way to build a similar getch function (see also several alternatives &c in the comment thread of that recipe).

Hydro answered 19/8, 2010 at 15:24 Comment(3)
It works as intended, and having a cross-platform solution is great. Thanks for answering!Virgo
Go to pypi.python.org/pypi/readchar directly, seems to do most of this, although I can't get it to read arrow keys on OSX properly.Overdress
What about Linux and Mac users? Any alternative?Codpiece
C
17

Python does not provide a multiplatform solution out of the box.
If you are on Windows you could try msvcrt with:

import msvcrt
print 'Press s or n to continue:\n'
input_char = msvcrt.getch()
if input_char.upper() == 'S': 
   print 'YES'
Competitive answered 19/8, 2010 at 15:29 Comment(1)
I've come across this module, but I need it to work on *nixes. Thanks anyway!Virgo
T
10

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))
Tonita answered 20/10, 2016 at 12:8 Comment(0)
P
7

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')
Penurious answered 1/7, 2022 at 6:46 Comment(1)
thank you, much needed answer without external libsRevisory
G
6

To get a single character, I have used getch, but I don't know if it works on Windows.

Gharry answered 16/5, 2016 at 0:14 Comment(1)
This is the simplest, getch not need enter, greatOratorian
T
4

use readchar:

import readchar

key = readchar.readkey()

if key == "a":
  print("text")

https://pypi.org/project/readchar/ to webpage

Tessin answered 30/9, 2022 at 10:41 Comment(0)
N
3

Instead of the msvcrt module you could also use WConio:

>>> import WConio
>>> ans = WConio.getkey()
>>> ans
'y'
Nonet answered 19/8, 2010 at 16:13 Comment(0)
E
1

On a side note, msvcrt.kbhit() returns a boolean value determining if any key on the keyboard is currently being pressed.

So if you're making a game or something and want keypresses to do things but not halt the game entirely, you can use kbhit() inside an if statement to make sure that the key is only retrieved if the user actually wants to do something.

An example in Python 3:

# this would be in some kind of check_input function
if msvcrt.kbhit():
    key = msvcrt.getch().decode("utf-8").lower() # getch() returns bytes data that we need to decode in order to read properly. i also forced lowercase which is optional but recommended
    if key == "w": # here 'w' is used as an example
        # do stuff
    elif key == "a":
        # do other stuff
    elif key == "j":
        # you get the point
Exorcise answered 22/2, 2016 at 16:45 Comment(0)
E
1

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()
Elson answered 14/8, 2019 at 23:24 Comment(0)
T
1

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.

Thury answered 3/3, 2021 at 6:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.