Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Ich möchte gerne einen Tastendruck abfangen. Konkret geht es mir hier um die Cursortasten, die in der Python-Shell (natürlich nach gestartetem Programm) abgefangen werden sollen.
Quasi sowas in die Richtung:
Wenn Event -> Tastaturcode so und so: mach das ...
das ganze natürlich ohne mit input auf eine Eingabe zu warten, der dann mit Return zu bestätigen ist.
Per Suche konnte ich bislang hauptsächlich Problemlösungen bezogen auf die GUI Programmierung finden.
@mcdaniels: Nein, das schaut leider wirklich nicht so gut aus. Da gibt's keinen allgemeinen, plattformübergreifenden Weg.
Unter Linux wäre die Antwort wohl das `curses`-Modul aus der Standardbibliothek, beziehungsweise `urwid` wenn es auch etwas zusätzlich Installierbares sein darf. Die `curses`-API ist nicht so der Hit.
Die folgende portable Funktion sollte genau das tun, was du haben möchtest. Das Drücken einer Pfeiltaste ergibt unter Unix eine Escape-Sequenz von 3 Zeichen. Dementsprechend müsstest du `length` anpassen. Probier halt einfach aus, ob bei einem zweiten Aufruf sofort eine Ausgabe kommt (das bedeutet: Ergebnis ist länger als 1 Zeichen) oder ob du wieder was neues eingeben kannst (Ergebnis ist genau 1 Zeichen lang).
try:
# windows
import msvcrt
except ImportError:
msvcrt = None
import sys
import termios
import tty
def getch(length=1):
"""
Read a key press and return the result. Nothing is echoed to the
console.
Note that on Windows a special function key press will return its
keycode. `Control-C` cannot be read there.
On Unices it will return one char by default. Thus, when reading
a special function key, whose resulting escape sequence could be
longer than one char, the `length` value might be changed, since
otherwise the remaining characters would be returned by the next
calls until stdin is "empty".
"""
if msvcrt:
char = msvcrt.getch()
if char in ('\000', '\xe0'):
# special key -> need a second call to get keycode
char = msvcrt.getch()
return char
else:
old = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin)
return sys.stdin.read(length)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old)
Übrigens, eventuell mache ich mir mal die Mühe, die möglichen Tasten(-kombinationen) plattformübergreifend mit Konstanten zu wrappen, damit man dafür nicht extra `pygame` installieren muss. Bedarf ist ja offenbar da und ich glaube, es gibt noch kein Modul dafür.
BTW: Weiß jemand einen sauberen Weg, um zu erkennen, ob noch Zeichen in STDIN sind? Mit `select` habe ich es nicht hinbekommen.
Traceback (most recent call last):
File "<pyshell#81>", line 1, in <module>
getch(y)
File "/home/user/tastenabfangen.py", line 32, in getch
old = termios.tcgetattr(sys.stdin)
TypeError: argument must be an int, or have a fileno() method.
Liefert jetzt auch bei Pfeiltasten ohne weiteres Zutun das korrekte Ergebnis. Python 3.x Kompatibilität möge man sich anpassen. Ich verwende jetzt zumindest Dateideskriptoren, anstatt einen `fileno()`-Aufruf zu erwarten, der bei Python 2.x ja funktioniert.
try:
# windows
import msvcrt
except ImportError:
msvcrt = None
import errno
import fcntl
import os
import sys
import termios
import tty
def getch():
"""
Read a key press and return the result. Nothing is echoed to the
console.
Note that on Windows a special function key press will return its
keycode. `Control-C` cannot be read there.
On Unices pressing a special key will return its escape sequence.
"""
if msvcrt:
char = msvcrt.getch()
if char in ('\000', '\xe0'):
# special key -> need a second call to get keycode
char = msvcrt.getch()
return char
else:
stdin = sys.stdin.fileno()
old = termios.tcgetattr(stdin)
try:
tty.setraw(stdin)
char = sys.stdin.read(1)
finally:
termios.tcsetattr(stdin, termios.TCSADRAIN, old)
old = fcntl.fcntl(stdin, fcntl.F_GETFL)
try:
fcntl.fcntl(stdin, fcntl.F_SETFL, old | os.O_NONBLOCK)
try:
char += sys.stdin.read()
except IOError, e:
if e.errno == errno.EAGAIN:
# there was no more than one char to read
pass
else:
raise
finally:
fcntl.fcntl(stdin, fcntl.F_SETFL, old)
return char
Auch ist Deine getch()-Funktion etwas seltsam, warum liest Du erst blockierend und nochmal nicht blockierend?
Um das plattformübergreifend zu implementieren, müsste man eine Art Keyhandler schreiben, der die entsprechenden Keycodes aus STDIN auswertet. Und genau hier liegt die Crux, aufgrund des Wildwuchses an Terminals sind die Keycodes sehr verschieden und man müsste zuvor den Terminaltypen abfragen, um die richtigen Keycodes zu suchen. Wenn Du das alles implementiert hast, hast Du quasi die Hälfte von curses nachgebaut