开发者

raw_input without pressing enter

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

c = raw_input('Press s or开发者_JAVA百科 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.


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


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


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'


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


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


Instead of the msvcrt module you could also use WConio:

>>> import WConio
>>> ans = WConio.getkey()
>>> ans
'y'


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


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


use readchar:

import readchar

key = readchar.readkey()

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

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜