[prog] Reading single keypresses in Python

Akkana Peck akkana at shallowsky.com
Sat Jun 19 16:31:42 UTC 2010


David Sumbler writes:
> I want to read a single keypress, to confirm or reject something - it's
> the usual sort of y/n kind of option, although in this case I want to
> use Enter/Esc as the alternatives.
> 
> input() is obviously not suitable.  The best idea I have come up with so
> far is 'sys.stdin.read(1)', but although this seems to work up to a
> point in an interactive session, I can't seem to get it to work in a
> program.  In any case, it still prints any characters typed, and I don't
> want anything sent to the screen until after either Enter or Esc has
> been pressed.

The most supported way to do it is through the curses package,
http://docs.python.org/release/2.6/library/curses.html
But that's fairly heavyweight if all you want to do is read a
character.

On Linux, the usual way to do that is to set the terminal's flags
to turn off echo (so you don't see the character typed) and turn
on cbreak (so you'll get characters as they're typed, instead of
buffering to the end of the line).

First, here's a quick hack doing it through stty:

======
import sys, os

print "Type a character:",
os.system("stty cbreak -echo")
sys.stdin.read(1)
os.system("stty -cbreak echo")
print "Thanks!"
======

It would be better, though, to set the terminal flags directly rather
than using os.system("stty"). And it turns out Python has a handy
routine for setting the terminal to cbreak mode:
http://docs.python.org/library/tty.html#module-tty
And at least on my system, it turns off echo as well, though the
docs don't say that.

Use it like this:

======
import sys, tty

print "Type a character:",
fd = sys.stdin.fileno()
tty.setcbreak(fd)
ch = sys.stdin.read(1)
print "Read", ch
======

The docs don't really explain that "when" argument, or how to set
the mode back, but in my little test program it resets the terminal
when I exit the program. There's some documentation of "when" under
termios: http://docs.python.org/library/termios.html
If you need to set the mode back before you exit the program, you
can do it with termios, like this:
======
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
tty.setcbreak(fd)
ch = sys.stdin.read(1)
print "Read", ch
termios.tcsetattr(fd, termios.TCSADRAIN, old)
======

If you find that it isn't consistent about turning off echo and you
need to do that separately, you can do that through termios. See:
http://docs.python.org/library/termios.html
and there's an example at the end of the page showing how to turn
off echo.

This is all Unix specific and may not work on Windows (though some
of it is POSIX so who knows? maybe it will work).  If you need it to
be cross-platform, you might have to use the curses module or at
least find out how curses handles echo and cbreak across platforms.

	...Akkana


More information about the Programming mailing list