La lecture d'un seul caractère (style getch) en Python ne fonctionne pas sous Unix
Question
Chaque fois que j'utilise la recette sur http://code.activestate.com/recipes/134892 / Je n'arrive pas à le faire fonctionner. Il génère toujours l'erreur suivante:
Traceback (most recent call last):
...
old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)
Ma meilleure pensée, c’est que c’est parce que je l’exécute dans Eclipse, de sorte que termios
jette un doute sur le descripteur de fichier.
La solution
Cela fonctionne sur Ubuntu 8.04.1, Python 2.5.2, je n’obtiens aucune erreur de ce type. Peut-être devriez-vous essayer à partir de la ligne de commande, eclipse peut utiliser son propre stdin, je reçois exactement la même erreur si je le lance à partir de Wing IDE, mais à partir de la ligne de commande, il fonctionne très bien. La raison est que IDE, par exemple Wing, utilise sa propre classe netserver.CDbgInputStream en tant que sys.stdin alors sys.stdin.fileno vaut zéro, c’est pourquoi l’erreur. En gros, IDE stdin n’est pas un tty (print sys.stdin.isatty () est 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()
Autres conseils
Mettre le terminal en mode brut n’est pas toujours une bonne idée. En fait, il suffit d'effacer le bit ICANON. Voici une autre version de getch () avec prise en charge de l'expiration du délai d'attente:
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()