I'm making an algorithm that reads input from keyboard, stores it on a variable called message and then write this variable into a file. However, whenever the user is typing, if he hits the ESC key, I'd like the execution to stop without any errors.
Let's say the code is:
message = raw_input()
What do I have to add? So if I'm in the middle of a sentence like:
My name is th
And I hit ESC, it stops?
There is no way to do this with normal input. raw_input
reads a whole line at a time.
In some (very few) cases, you can do this:
message = ''
while True:
ch = sys.stdin.read(1)
if ch == '\x1b':
exit(0)
elif ch == '\n':
break
message += ch
But in general, this won't work. For example, on a typical Unix system, sys.stdin
will be line-buffered, and possibly even fed through a library like readline
to allow the user to edit as he goes along. Or, if you're running the program inside IDLE, there is no way to read stdin
at all; raw_input
works around that by popping up a dialog box asking for input, but your code can't do that.
You can work around that in different ways on different platforms in different situations.
On Windows, if you know your input is going to be a "DOS prompt" window (which you can check with sys.stdin.isatty()
), you can use the msvcrt
functions. For example:
import sys, msvcrt
assert sys.stdin.isatty(), "Can't run without a console to run on"
message = u''
while True:
ch = msvcrt.getwche()
if ch == u'\x1b':
exit(0)
elif ch == u'\n':
break
message += ch
This should work for both 2.6+ and 3.3+, but in 2.x, unlike raw_input
, it returns a unicode
instead of a str
. If you want str
here, drop all the u
prefixes and use getche
instead of getwche
.
On most POSIX-like platforms with reasonably-standard termios
(including Mac OS X and Linux), if you know your input is going to be a "TTY" (which you can check with sys.stdin.isatty()
—or, if you prefer, you can just look for a TTY to use instead of stdin, although that doesn't work on quite as many platforms/in quite as many situations), you can use the [termios
](http://docs.python.org/3/library/termios.html) or tty
module to put the input into "raw" mode. In Python 3.x, you will probably have to read directly from sys.stdin.buffer
instead of sys.stdin
, and decode to Unicode manually. So:
import sys, termios, tty
assert sys.stdin.isatty(), "Can't run without a console to run on"
fd = sys.stdin.fileno()
stash = termios.tcgetattr(fd)
try:
tty.setraw(fd)
newterm = termios.tcgetattr(fd)
newterm[tty.LFLAG] |= termios.ECHO
termios.tcsetattr(fd)
message = b''
while True:
ch = sys.stdin.buffer.read(1)
if ch == b'\x1b':
exit(0)
elif ch == b'\n':
break
else:
message += ch
message = message.decode(sys.stdin.encoding)
finally:
termios.tcsetattr(fd, termios.TCSANOW, stash)
As with the Windows version, this should be 2.6+/3.3+ multi-version-compatible, except for the fact that it always returns unicode
while 2.x raw_input
would return str
(which in this case is all in that one decode
line, which you can drop if you don't want it).
Notice that I used both tty
and termios
here. The tty
module is a higher-level wrapper, but it doesn't do everything you want. So, you can use it as far as it goes (to flip whatever switches are needed to get raw mode on your platform, and to let you use cross-platform/readable names for the flagsets and values instead of indices), but you usually still need termios
anyway.
On any other platform, you're on your own.