Leer un solo personaje (estilo getch) en Python no funciona en Unix
Pregunta
Cada vez que uso la receta en http://code.activestate.com/recipes/134892 / Parece que no puedo hacer que funcione. Siempre arroja el siguiente error:
Traceback (most recent call last):
...
old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)
Mi mejor pensamiento es que es porque lo estoy ejecutando en Eclipse, por lo que termios
está encajando con el descriptor de archivos.
Solución
Esto está funcionando en Ubuntu 8.04.1, Python 2.5.2, no obtengo tal error. Puede ser que deba probarlo desde la línea de comandos, eclipse puede estar usando su propio stdin, obtengo exactamente el mismo error si lo ejecuto desde Wing IDE, pero desde la línea de comandos funciona muy bien. La razón es que IDE, por ejemplo, Wing está utilizando su propia clase netserver.CDbgInputStream como sys.stdin entonces sys.stdin.fileno es cero, por eso el error. Básicamente, IDE stdin no es un tty (print sys.stdin.isatty () es False)
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
getch = _GetchUnix()
print getch()
Otros consejos
Poner el terminal en modo sin procesar no siempre es una buena idea. En realidad, es suficiente para borrar el bit ICANON. Aquí hay otra versión de getch () con soporte de tiempo de espera:
import tty, sys, termios
import select
def setup_term(fd, when=termios.TCSAFLUSH):
mode = termios.tcgetattr(fd)
mode[tty.LFLAG] = mode[tty.LFLAG] & ~(termios.ECHO | termios.ICANON)
termios.tcsetattr(fd, when, mode)
def getch(timeout=None):
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
setup_term(fd)
try:
rw, wl, xl = select.select([fd], [], [], timeout)
except select.error:
return
if rw:
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
if __name__ == "__main__":
print getch()