Saturday, November 21, 2009

Getch in Python - Read a Character without Enter

Sometimes it is more appropriate to read from keyboard without waiting for Enter to be codessed, for example, when choosing from a menu. The raw_input("Prompt String >") always waits for Enter, so a function similar to getch() in C is needed and there are some solutions for that in Python.
  1. There is getch() equivalent for Windows environment in library msvcrt:

    import msvcrt

    result = msvcrt.getch()

    Funny thing is that the code works fine from command window (cmd.exe), but does not in IDLE: in IDLE it does not wait for a key and gives out '\xff' as the result.

    In Linux the same can be done this way:

    import sys, tty, termios

    fd = sys.stdin.fileno()
    # save original terminal settings
    old_settings = termios.tcgetattr(fd)

    # change terminal settings to raw read
    tty.setraw(sys.stdin.fileno())

    ch = sys.stdin.read(1)

    # restore original terminal settings
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    print '\ncodessed char is \'' + ch +'\'\n'

    Since the solution is system dependent some people propose a universal approach when program tries one implementation and if it fails - the other one, here is an example from ActiveState Code, Danny Yoo:

    class _Getch:
    """Gets a single character from standard input.
    Does not echo to the screen."""
    def __init__(self):
    try:
    self.impl = _GetchWindows()
    except ImportError:
    self.impl = _GetchUnix()

    def __call__(self): return self.impl()

    class _GetchUnix:
    def __init__(self):
    import tty, sys

    def __call__(self):
    import sys, tty, termios
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
    tty.setraw(sys.stdin.fileno())
    ch = sys.stdin.read(1)
    finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return ch

    class _GetchWindows:
    def __init__(self):
    import msvcrt

    def __call__(self):
    import msvcrt
    return msvcrt.getch()

    getch = _Getch()

  2. Using TK library the following code works OK:

    import Tkinter as tk

    def keycodess(event):
    if event.keysym == 'Escape':
    root.destroy()
    x = event.char
    if x == "a":
    print "A done"
    elif x == "b":
    print "B done"
    elif x == "c":
    print "C done"
    elif x == "d":
    print "D done"
    else:
    print x

    root = tk.Tk()
    print "a. Menu choice A"
    print "b. Menu choice B"
    print "c. Menu choice C"
    print "d. Menu choice D"
    root.bind_all('', keycodess)
    # hiding the tk window
    root.withdraw()
    root.mainloop()

    It works OK in Windows command window (cmd.exe), but in IDLE and Linux the line root.withdraw() should be commented out - the code works OK while the root window is in focus. This kind of examples you can find here.

  3. Curses library has its own getch implementation. I did not check it, just would like to mention for the record.
If you know any other solutions - I'd love to here from you about them!

0 comments:

Post a Comment