Чтение одного символа (getch style) в Python не работает в Unix
Вопрос
Каждый раз, когда я использую рецепт в http://code.activestate.com/recipes/134892/ Кажется, я не могу заставить это работать.Он всегда выдает следующую ошибку:
Traceback (most recent call last):
...
old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)
Моя лучшая мысль заключается в том, что это потому, что я запускаю его в Eclipse, так что termios
закатывает истерику по поводу файлового дескриптора.
Решение
Это работает на Ubuntu 8.04.1, Python 2.5.2, я не получаю такой ошибки.Возможно, вам следует попробовать это из командной строки, eclipse может использовать свой собственный stdin, я получаю точно такую же ошибку, если запускаю ее из Wing IDE, но из командной строки она отлично работает.Причина в том, что IDE, например Wing, использует собственный класс netserver.CDbgInputStream как sys.stdin итак, sys.stdin.fileno равно нулю, вот почему ошибка.По сути, IDE stdin не является tty (print sys.stdin.isatty() имеет значение 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()
Другие советы
Перевод терминала в необработанный режим не всегда является хорошей идеей.На самом деле этого достаточно, чтобы немного очистить ICANON.Вот другая версия getch() с поддержкой тайм-аута:
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()