Seite 1 von 1

Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 07:21
von mcdaniels
Hallo und guten Morgen!

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.

Wird wohl auf

Code: Alles auswählen

sys.stdin
rauslaufen.

Hm.. Gerade das hier gefunden: http://www.python-forum.de/viewtopic.ph ... =sys.stdin

Schaut wohl nicht so gut aus...

Danke und LG

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 08:13
von BlackJack
@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.

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 09:17
von snafu
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).

Code: Alles auswählen

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)

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 09:49
von snafu
Ü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.

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 10:39
von mcdaniels
Hey snafu!

Dumme Frage, läuft das auch unter Python 3.x?

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.

LG

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 10:56
von snafu
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.

Code: Alles auswählen

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

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 11:02
von mcdaniels
Python 3.x Kompatibilität möge man sich anpassen
O lala, so weit bin ich lange noch nicht ;-)

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 14:16
von jerch
snafu hat geschrieben:BTW: Weiß jemand einen sauberen Weg, um zu erkennen, ob noch Zeichen in STDIN sind? Mit `select` habe ich es nicht hinbekommen.
Ja das geht mit select und poll. siehe http://www.python-forum.de/viewtopic.php?f=11&t=20555

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 ;)

Re: Tastendruck abfangen

Verfasst: Donnerstag 26. August 2010, 19:53
von mcdaniels
Guten Abend!

Nundenn, ich lege die Tastenabfrage somit lieber mal auf Eis und widme mich weiterhin den Basics :-)

LG