A leitura de um único caractere (estilo getch) em Python não está funcionando em Unix
Pergunta
Toda vez que eu usar a receita em http://code.activestate.com/recipes/134892 / eu não consigo fazê-lo funcionar. É sempre lança o seguinte erro:
Traceback (most recent call last):
...
old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)
O meu melhor pensamento é que é porque eu estou correndo em Eclipse para termios
está jogando um ajuste sobre o descritor de arquivo.
Solução
Este está trabalhando em Ubuntu 8.04.1, Python 2.5.2, recebo nenhum tal erro. Pode ser que você deve experimentá-lo a partir da linha de comando, eclipse pode estar usando seu próprio stdin, eu recebo exatamente o mesmo erro se eu executá-lo a partir Ala IDE, mas a partir de linha de comando que funciona muito bem. A razão é que IDE por exemplo Ala está usando lá própria classe netserver.CDbgInputStream como sys.stdin assim sys.stdin.fileno é zero, é por isso que o erro. Basicamente IDE stdin não é um tty (sys.stdin.isatty print () é 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()
Outras dicas
Colocando o terminal em modo RAW não é sempre uma boa idéia. Na verdade, é o suficiente para pouco ICANON clara. Aqui está outra versão do getch () com o apoio timeout:
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()