2008-11-15 19 views
56

Jak mogę odpytać klawiaturę za pomocą aplikacji python? Konkretnie chciałbym zrobić coś podobnego do tego w środku wiele innych I/O (Wybiera działalności Gniazdo, dostępu do portu szeregowego, etc.):Odpytywanie klawiatury (wykrywanie naciśnięcia klawisza) w pythonie

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 

Jaki jest prawidłowy pythonic sposób zrobić to w systemie Windows? Przenoszenie do Linuksa nie byłoby złe, chociaż nie jest wymagane.

+0

Tylko niech inni ludzie wiedzą, stwierdziliśmy, że większość rozwiązań obejmujących wybrać lub wątku bibliotek nie działa poprawnie z bezczynności. Jednakże _ ** wszystkie ** _ działały poprawnie na CLI, tj. 'Python/home/pi/poll_keyboard.py' – davidhood2

Odpowiedz

26

Standardowe podejście polega na użyciu modułu select.

Jednak to nie działa w systemie Windows. W tym celu można skorzystać z modułu ankietowego modułu msvcrt.

Często odbywa się to za pomocą wielu wątków - po jednym na każde urządzenie "oglądane" oraz w tle, które mogą wymagać przerwania przez urządzenie.

6

Możesz sprawdzić, jak pygame radzi sobie z tym, aby wykraść niektóre pomysły.

5

Z uwag:

import msvcrt # built-in module 

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

Dzięki za pomoc. Skończyło się na pisanie DLL C nazwie PyKeyboardAccess.dll i uzyskiwania dostępu do funkcji crt Conio, eksport to rutyna:

#include <conio.h> 

int kb_inkey() { 
    int rc; 
    int key; 

    key = _kbhit(); 

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

    return rc; 
} 

A ja do niego dostęp w python za pomocą modułu ctypes (wbudowane w Pythonie 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

Jak to jest lepsze niż wbudowane msvcrt.kbhit()? Jaką ma to przewagę? –

+0

Masz absolutną rację! Źle odczytałem twój post; Nie zdawałem sobie sprawy, że istnieje moduł python zwany msvcrt! Po prostu myślałem, że masz na myśli "użyj ms crt", a potem zacząłem myśleć o wątkach i nie łączyłem kropek. Masz absolutną rację. –

+1

I tak samo z: importu msvcrt def kbfunc(): X = msvcrt.kbhit () jeśli x: = retencji ORD (msvcrt.getch()) innego: retencji = 0 return ret –

14

Ok, ponieważ moja próba opublikowania mojego rozwiązania w komentarzu nie powiodła się, oto, co próbowałem powiedzieć. Mogłem zrobić dokładnie to, co chciałem od rodzimej Pythonie (na Windows, choć nie gdziekolwiek indziej) z następującego kodu:

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

brak pracy. dostałem błąd: select.error: (10093, "Albo aplikacja nie wywołała WSAStartup, albo WSAStartup nie powiodło się") – DarenW

+1

Słyszałem, więcej niż kilka razy, że wybrane wywołanie systemowe w MS Windows nie obsługuje zwykłego pliku deskryptory i działa tylko na gniazdach. (Nie wiem, czy implementacja select() w Pythonie kiedykolwiek działała pod tym oknem). –

+0

po co jest ten dziwny limit czasu? Działa dla mnie z timeout = 0, ale nie z 0,0001, jak pokazano .. – frans

13

Rozwiązanie za pomocą modułu curses. Drukowanie wartości liczbowe odpowiadające każdemu naciśnięciu klawisza:

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

To nie działa w systemie Windows ... – Oz123

+0

OZ123: Może. Zobacz http://stackoverflow.com/questions/32417379/what-is-iseded-for-curses-in-python-3-4-on-windows7 –

+0

Wystąpiły problemy z używaniem przekleństw przez termin SSH na hostach bez nagłówków. Problemy poważnie zepsuły terminal - wymagając, aby był "reset" między każdym biegiem. To zadziałało, tj. Wykryło naciśnięcie klawisza. Musi istnieć mądrzejsze rozwiązanie. – Mark

0

Jeśli połączyć time.sleep, threading.Thread i sys.stdin.read łatwo można czekać na określoną ilość czasu na wejście, a następnie kontynuować, również powinno to być kompatybilne z wieloma platformami.

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

Można również umieścić to w zależności jak tak

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 

Mimo to nic nie powróci więc zamiast należy użyć modułu basen wieloprocesorowe może się okazać, że tutaj: how to get the return value from a thread in python?

+0

Czy ten pierwszy wiersz nie powinien być: t = wątek.Thread (target = sys.stdin.read, args = (1,)) –

+0

Czy to rozwiązanie zawsze będzie spać przez 5 sekund, nawet jeśli użytkownik naciśnie klawisz przed że? –

7

Żadna z tych odpowiedzi nie sprawdziła się u mnie. Ten pakiet, pynput, robi dokładnie to, czego potrzebuję.

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

To zadziałało dla mnie, z wyjątkiem tego, że naciśnięty klawisz został odtworzony na ekranie natychmiast po naciśnięciu i nie było sposobu, aby go wyłączyć. https://github.com/moses-palmer/pynput/issues/47 Dodatkowo, znaki są buforowane i pojawiają się dodatkowo w linii poleceń, gdy program również się kończy. To wydaje się być ograniczeniem implementacji Linuksa , ale działa dobrze w systemie Windows. – Trevor

+0

To rozwiązanie nie działa, gdy skrypt działa przez ssh. Wyskakuje z błędem: "Xlib.error.DisplayNameError: Zła nazwa wyświetlana" "." –

+0

Jak wspomniano powyżej, David: nie jest to dobre rozwiązanie dla instancji bezgłowych, ponieważ ma zależność od Xserver. 'importuj Xlib.display' – Mark

Powiązane problemy