2008-11-15 7 views
56

Wie kann ich die Tastatur von einer Konsole Python-App abfragen? Insbesondere würde Ich mag so etwas wie dies in der Mitte einer Menge anderer E/A-Aktivitäten tun (Buchse wählt, seriell Port, etc.):Polling der Tastatur (erkennen Sie einen Tastendruck) in Python

while 1: 
     # doing amazing pythonic embedded stuff 
     # ... 

     # periodically do a non-blocking check to see if 
     # we are being told to do something else 
     x = keyboard.read(1000, timeout = 0) 

     if len(x): 
      # ok, some key got pressed 
      # do something 

Was den richtigen pythonic Weg ist zu tun das unter Windows? Auch die Portabilität zu Linux wäre nicht schlecht, obwohl es nicht erforderlich ist.

+0

einfach andere Leute wissen zu lassen, fand ich, dass die meisten Lösungen wählen oder Thread-Bibliotheken an denen nicht von IDLE richtig funktionierte. Aber sie _ ** alle ** _ funktionierte gut auf der CLI, d. H. 'Python/home/pi/poll_keyboard.py' – davidhood2

Antwort

26

Der Standardansatz ist die Verwendung des Moduls select.

Dies funktioniert jedoch nicht auf Windows. Dazu können Sie die Tastaturabfrage des Moduls msvcrt verwenden.

Oft wird dies mit mehreren Threads durchgeführt - einer pro Gerät wird "überwacht" und die Hintergrundprozesse, die möglicherweise vom Gerät unterbrochen werden müssen.

6

Sie könnten sehen, wie pygame dies handhabt, um einige Ideen zu stehlen.

5

Aus den Kommentaren:

import msvcrt # built-in module 

def kbfunc(): 
    return ord(msvcrt.getch()) if msvcrt.kbhit() else 0 

Danke für die Hilfe. Ich landete das Schreiben eines C DLL PyKeyboardAccess.dll und den Zugriff auf die crt conio Funktionen, exportieren diese Routine aufgerufen:

#include <conio.h> 

int kb_inkey() { 
    int rc; 
    int key; 

    key = _kbhit(); 

    if (key == 0) { 
     rc = 0; 
    } else { 
     rc = _getch(); 
    } 

    return rc; 
} 

Und ich zugreifen es in Python mit dem ctypes Modul (eingebaut in Python 2.5):

import ctypes 
import time 

# 
# first, load the DLL 
# 


try: 
    kblib = ctypes.CDLL("PyKeyboardAccess.dll") 
except: 
    raise ("Error Loading PyKeyboardAccess.dll") 


# 
# now, find our function 
# 

try: 
    kbfunc = kblib.kb_inkey 
except: 
    raise ("Could not find the kb_inkey function in the dll!") 


# 
# Ok, now let's demo the capability 
# 

while 1: 
    x = kbfunc() 

    if x != 0: 
     print "Got key: %d" % x 
    else: 
     time.sleep(.01) 
+0

Wie ist das besser als die integrierte msvcrt.kbhit()? Welchen Vorteil hat es? –

+0

Sie haben absolut Recht! Ich habe deinen Beitrag falsch gelesen; Ich habe nicht bemerkt, dass es ein Python-Modul namens msvcrt gibt! Ich dachte nur, du meintest "benutze die ms crt", und dann wurde ich dazu gebracht, über Threads nachzudenken und verband die Punkte nicht. Du liegst absolut richtig. –

+1

Ich tat das Gleiche mit: Import msvcrt def kbfunc(): x = msvcrt.kbhit() wenn x: ret = ord (msvcrt.getch()) sonst: ret = 0 return ret –

14

Ok, da mein Versuch, meine Lösung in einem Kommentar zu veröffentlichen fehlgeschlagen ist, hier ist, was ich versuchte zu sagen. Ich konnte tun genau das, was ich aus nativen Python (unter Windows, nirgendwo sonst aber) mit dem folgenden Code gesucht:

import msvcrt 

def kbfunc(): 
    x = msvcrt.kbhit() 
    if x: 
     ret = ord(msvcrt.getch()) 
    else: 
     ret = 0 
    return ret 
16

import sys 
import select 

def heardEnter(): 
    i,o,e = select.select([sys.stdin],[],[],0.0001) 
    for s in i: 
     if s == sys.stdin: 
      input = sys.stdin.readline() 
      return True 
    return False 
+0

keine Arbeit. Fehler: select.error: (10093, 'Entweder hat die Anwendung WSAStartup nicht aufgerufen, oder WSAStartup ist fehlgeschlagen') – DarenW

+1

Ich habe gehört, mehr als ein paar Mal, dass der ausgewählte Systemaufruf unter MS Windows keine reguläre Datei unterstützt Deskriptoren und funktioniert nur auf Sockets. (Ich weiß nicht, ob die Python-Implementierung von select() jemals unter der Haube funktioniert hat). –

+0

Für was ist diese seltsame Auszeit? Es funktioniert für mich mit timeout = 0 aber nicht mit 0,0001 wie gezeigt. – frans

13

Eine Lösung mit den Flüchen Modul. Drucken über einen numerischen Wert jeder Taste entspricht gedrückt:

import curses 

def main(stdscr): 
    # do not wait for input when calling getch 
    stdscr.nodelay(1) 
    while True: 
     # get keyboard input, returns -1 if none available 
     c = stdscr.getch() 
     if c != -1: 
      # print numeric value 
      stdscr.addstr(str(c) + ' ') 
      stdscr.refresh() 
      # return curser to start position 
      stdscr.move(0, 0) 

if __name__ == '__main__': 
    curses.wrapper(main) 
+1

Dies funktioniert nicht auf Windows ... – Oz123

+0

OZ123: Es kann. Siehe http://StackOverflow.com/Questions/32417379/what-is-needed-for-curses-in-python-3-4-on-windows7 –

+0

Hatte Probleme mit Flüche über SSH-Begriff auf Headless-Host. Das Terminal wurde durch Probleme stark in Unordnung gebracht - es musste zwischen den einzelnen Vorgängen "zurückgesetzt" werden. Es hat funktioniert, d.h. Tastendruck erkannt. Es muss eine klügere Lösung geben. – Mark

0

Wenn Sie kombinieren time.sleep, threading.Thread und sys.stdin.read Sie leicht für eine bestimmte Menge an Zeit für die Eingabe warten und dann weiter, Auch dies sollte plattformübergreifend kompatibel sein.

t = threading.Thread(target=sys.stdin.read(1) args=(1,)) 
t.start() 
time.sleep(5) 
t.join() 

Sie könnten auch

def timed_getch(self, bytes=1, timeout=1): 
    t = threading.Thread(target=sys.stdin.read, args=(bytes,)) 
    t.start() 
    time.sleep(timeout) 
    t.join() 
    del t 

wie so diese in eine Funktion legen Obwohl dies alles nicht zurückkehren wird, so stattdessen sollten Sie die Multiprocessing-Pool-Modul nutzen Sie das hier finden: how to get the return value from a thread in python?

+0

Sollte diese erste Zeile nicht sein: t = threading.Thread (target = sys.stdin.read, args = (1,)) –

+0

Wird diese Lösung nicht immer für 5 Sekunden schlafen, auch wenn der Benutzer zuvor eine Taste drückt Das? –

7

Keine dieser Antworten funktionierte gut für mich. Dieses Paket, pynput, macht genau das, was ich brauche.

https://pypi.python.org/pypi/pynput

from pynput.keyboard import Key, Listener 

def on_press(key): 
    print('{0} pressed'.format(
     key)) 

def on_release(key): 
    print('{0} release'.format(
     key)) 
    if key == Key.esc: 
     # Stop listener 
     return False 

# Collect events until released 
with Listener(
     on_press=on_press, 
     on_release=on_release) as listener: 
    listener.join() 
+0

Dies funktionierte für mich, außer dass die gedrückte Taste sofort nach dem Drücken auf dem Bildschirm angezeigt wurde und es keine Möglichkeit gab, sie zu deaktivieren. https://github.com/moses-palmer/pynput/issues/47 Außerdem sind die Zeichen gepuffert und erscheinen zusätzlich in der Befehlszeile, wenn das Programm beendet wird. Dies scheint eine Einschränkung der Linux-Implementierung zu sein , aber es funktioniert gut unter Windows. – Trevor

+0

Diese Lösung funktioniert nicht, wenn das Skript über ssh ausgeführt wird. Es bombardiert mit dem Fehler: 'Xlib.error.DisplayNameError: Ungültiger Anzeigename "".' –

+0

Wie oben von David erwähnt - dies ist keine gute Lösung für Headless-Instanzen, da es eine Abhängigkeit von Xserver hat. 'importiere Xlib.display' – Mark