wykryć naciśnięcie klawisza w Pythonie?

106

Robię program typu stoper w pythonie i chciałbym wiedzieć, jak wykryć, czy klawisz jest wciśnięty (np. P dla pauzy i s dla zatrzymania), a nie chciałbym, aby było to coś w rodzaju raw_input, które czeka na dane wejściowe użytkownika przed kontynuowaniem wykonywania. Czy ktoś wie, jak to zrobić w pętli while?

Chciałbym również stworzyć tę platformę wieloplatformową, ale jeśli nie jest to możliwe, moim głównym celem rozwoju jest linux

lobuo
źródło
dla OS X stackoverflow.com/a/47197390/5638869 działa w Pythonie 2 i 3
neoDev

Odpowiedzi:

71

Python ma moduł klawiatury z wieloma funkcjami. Zainstaluj go, być może za pomocą tego polecenia:

pip3 install keyboard

Następnie użyj go w kodzie takim jak:

import keyboard  # using module keyboard
while True:  # making a loop
    try:  # used try so that if user pressed other than the given key error will not be shown
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            print('You Pressed A Key!')
            break  # finishing the loop
    except:
        break  # if user pressed a key other than the given key the loop will break
Społeczność
źródło
2
Nie jestem pewien co do Linuksa, ale dla mnie działa w systemie Windows.
74
keyboardnajwyraźniej wymaga
rootowania
Wypróbowałem to rozwiązanie, ale kiedy próbuję zaimportować moduł po jego zainstalowaniu, pojawia się komunikat „ImportError: Brak modułu o nazwie„ klawiatura ””, więc to nie działa. Sprawdziłem w repozytorium GitHub i znalazłem powiązany problem , ale to nie rozwiązuje problemu. Następnie próbowałem pobrać repozytorium i wykonać niektóre z jego przykładów, ale otrzymałem komunikat „ImportError: Musisz być rootem, aby korzystać z tej biblioteki w systemie Linux”, jak wcześniej skomentował @Inaimathi. Wygląda na to, że jest to pełny moduł do zarządzania klawiaturą w Pythonie, ale wymaganie roota to duży brak :(
Ivanhercaz
3
„Aby uniknąć uzależnienia od X, części Linuksa odczytują surowe pliki urządzeń (/ dev / input / input *), ale to wymaga root”.
jrouquie
8
Nie widzę powodu, dla którego próba: z wyjątkiem: byłaby przydatna.
TypicalHog,
49

Dla tych, którzy są na oknach i próbowali znaleźć działającą odpowiedź, oto moja: 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()

Powyższa funkcja wydrukuje dowolny klawisz, który naciśniesz, oraz rozpocznie akcję po zwolnieniu klawisza „esc”. Dokumentacja klawiatury jest tutaj dla bardziej zróżnicowanych zastosowań.

Markus von Broady zwrócił uwagę na potencjalny problem: Ta odpowiedź nie wymaga, abyś był w bieżącym oknie, aby ten skrypt został aktywowany, rozwiązaniem dla okien byłoby:

from win32gui import GetWindowText, GetForegroundWindow
current_window = (GetWindowText(GetForegroundWindow()))
desired_window_name = "Stopwatch" #Whatever the name of your window should be

#Infinite loops are dangerous.
while True: #Don't rely on this line of code too much and make sure to adapt this to your project.
    if current_window == desired_window_name:

        with Listener(
            on_press=on_press,
            on_release=on_release) as listener:
            listener.join()
Mitrek
źródło
7
@ nimig18 ... i nie wymaga rootowania :)
cz
1
Jest problem z tym rozwiązaniem (brak pewności co do alternatyw): aby zadziałało, nie trzeba naciskać klawisza w oknie konsoli. Wyobraź sobie skrypt, który wykonuje jakąś pracę do momentu naciśnięcia klawisza ESC, a następnie naciśnięcia go w innym programie.
Markus von Broady
1
@MarkusvonBroady Myślę, że do rozwiązania tego problemu wystarczyłoby win32gui, zredagowałem odpowiedź w sposób, który potencjalnie mógłby rozwiązać problem przynajmniej dla użytkowników Windowsa.
Mitrek
@Mitrek Próbowałem tego, ale mój kod zatrzymuje dalsze wykonywanie i utknął tutaj. Działa jak input (). Mam kod wykonujący się w selenie, firefoxie, ale jak tylko ta sekwencja zostanie napotkana, nie ma dalszej akcji.
Lakshmi Narayanan
1
Powinna być akceptowana odpowiedź, ponieważ działa zarówno w systemie Linux, jak i Windows
Akash Karnatak
31

Jako OP wspomina o raw_input - to znaczy, że chce rozwiązania CLI. Linux: curses jest tym, czego chcesz (Windows PDCurses). Curses to graficzny interfejs API oprogramowania CLI, dzięki któremu można osiągnąć więcej niż tylko wykrywanie kluczowych zdarzeń.

Ten kod będzie wykrywał klawisze do momentu naciśnięcia nowej linii.

import curses
import os

def main(win):
    win.nodelay(True)
    key=""
    win.clear()                
    win.addstr("Detected key:")
    while 1:          
        try:                 
           key = win.getkey()         
           win.clear()                
           win.addstr("Detected key:")
           win.addstr(str(key)) 
           if key == os.linesep:
              break           
        except Exception as e:
           # No input   
           pass         

curses.wrapper(main)
Abc Xyz
źródło
To jest naprawdę miłe. Musiałem szukać wiecznie, zanim go spotkałem. Wydaje się znacznie czystsze niż hakowanie z termiosi tak dalej ...
Hugh Perkins
5
trzeba dodać import os, aby móc wyjść z przykładu.
malte
Jeśli nie win.nodelay(False)zamiast True, to nie będzie generował milion wyjątków na sekundę.
Johannes Hoff
25

Jest więcej rzeczy, które można zrobić z keyboardmodułem.

Oto kilka metod:


Metoda nr 1:

Korzystanie z funkcji read_key():

import keyboard

while True:
    if keyboard.read_key() == "p":
        print("You pressed p")
        break

To przerwie pętlę po naciśnięciu pklawisza.


Metoda nr 2:

Korzystanie z funkcji wait:

import keyboard

keyboard.wait("p")
print("You pressed p")

Będzie czekał, aż naciśniesz pi będzie kontynuował naciśnięcie kodu.


Metoda nr 3:

Korzystanie z funkcji on_press_key:

import keyboard

keyboard.on_press_key("p", lambda _:print("You pressed p"))

Potrzebuje funkcji zwrotnej. Użyłem, _ponieważ funkcja klawiatury zwraca zdarzenie klawiatury do tej funkcji.

Po wykonaniu uruchomi funkcję po naciśnięciu klawisza. Możesz zatrzymać wszystkie zaczepy, uruchamiając tę ​​linię:

keyboard.unhook_all()

Metoda nr 4:

Na tę metodę w pewnym sensie odpowiedział już user8167727, ale nie zgadzam się z kodem, który stworzyli. Będzie używać funkcji, is_pressedale w inny sposób:

import keyboard

while True:
    if keyboard.is_pressed("p"):
        print("You pressed p")
        break

Po pnaciśnięciu nastąpi przerwanie pętli .


Uwagi:

  • keyboard odczyta naciśnięcia klawiszy z całego systemu operacyjnego.
  • keyboard wymaga roota w systemie Linux
Czarny grzmot
źródło
11
Największą NEGATYWĄ używania modułu klawiatury jest to, że musisz działać jako użytkownik ROOT. To sprawia, że ​​moduł jest verboten w moim kodzie. Samo sprawdzenie, czy klawisz został naciśnięty, nie wymaga uprawnień roota. Przeczytałem dokumentację i rozumiem, dlaczego ograniczenie wychodzi w module, ale poszukaj gdzie indziej, czy wystarczy
odpytać
Bardzo pomocne informacje udostępnione, Sir! Chciałem wiedzieć, czy mogę użyć keyboard.wait()czekania na więcej niż 1 klawisz i kontynuować, jeśli którykolwiek z nich zostanie naciśnięty
Preetkaran Singh
@PreetkaranSingh wait()nie daje takiej funkcjonalności. Będziesz musiał użyć keyboard.read_key()warunku if spakowanego w pętli while. Zobacz metodę nr 1
Black Thunder
Dzięki Sir !, czy chciałbyś rzucić trochę światła na suppressużycie słowa kluczowego keyboard.read_key(), kiedy go używać, a kiedy nie ...
Preetkaran Singh
@PreetkaranSingh Chciałbym, ale nie mam wystarczających informacji na temat argumentu tłumiącego
Black Thunder
14

W przypadku systemu Windows możesz użyć msvcrttego:

   import msvcrt
   while True:
       if msvcrt.kbhit():
           key = msvcrt.getch()
           print(key)   # just to show the result
Benjie
źródło
7
msvcrt to moduł tylko dla systemu Windows.
Dunatotatos
1
Właściwie używam teraz pynput, to może być lepsza odpowiedź
Benjie,
Zauważ, że pynput, aby działał na OS X (nie wiem o Linuksie), musi działać jako root, aby działać. To może być nie starter dla niektórych ludzi.
Gabe Weiss
Mógłbym przysiąc, że pytanie dotyczyło „cross-platform” lub „linux” ...
Aaron Mann
10

Użyj tego kodu, aby znaleźć naciśnięty klawisz

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {0} pressed'.format(
            key.char))
    except AttributeError:
        print('special key {0} pressed'.format(
            key))

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

# Collect events until released
with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()
Manivannan Murugavel
źródło
Oto jednak rzecz, używam macOS i zainstalowałem osobno pynput i klawiaturę, a program działa bez żadnych błędów, ale może wykryć tylko (w powłoce Pythona) specjalne klawisze. Klawisze alfanumeryczne nie są wykrywane, a wręcz przeciwnie, są traktowane tak, jakbym pisał kod na powłoce. Czy wiesz, jaki może być problem?
Dario Deniz Ergün
Ten sam kod działał dla mnie w powłoce. Proszę sprawdź to. Pakiet klawiatury nie wymaga tego kodu.
Manivannan Murugavel
1
To jest droga w Linuksie, ponieważ biblioteka klawiatury wymaga roota.
David
1
To rozwiązanie wykryje wszystkie naciśnięcia klawiszy; także te, które mają miejsce w innym oknie terminala. Niestety, poważnie ogranicza to możliwe przypadki użycia.
Serge Stroobandt
6

Użyj PyGame, aby mieć okno, a następnie możesz uzyskać kluczowe wydarzenia.

Do listu p:

import pygame, sys
import pygame.locals

pygame.init()
BLACK = (0,0,0)
WIDTH = 1280
HEIGHT = 1024
windowSurface = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)

windowSurface.fill(BLACK)

while True:
    for event in pygame.event.get():
        if event.key == pygame.K_p: # replace the 'p' to whatever key you wanted to be pressed
             pass #Do what you want to here
        if event.type == pygame.locals.QUIT:
             pygame.quit()
             sys.exit()
AJ Uppal
źródło
2

Zrobiłem więc ten… rodzaj gry… na podstawie tego postu (używając biblioteki msvcr i Pythona 3.7).

Oto "główna funkcja" gry, czyli wykrywanie wciśniętych klawiszy:

# Requiered libraries - - - -
import msvcrt
# - - - - - - - - - - - - - -


def _secret_key(self):
    # Get the key pressed by the user and check if he/she wins.

    bk = chr(10) + "-"*25 + chr(10)

    while True:

        print(bk + "Press any key(s)" + bk)
        #asks the user to type any key(s)

        kp = str(msvcrt.getch()).replace("b'", "").replace("'", "")
        # Store key's value.

        if r'\xe0' in kp:
            kp += str(msvcrt.getch()).replace("b'", "").replace("'", "")
            # Refactor the variable in case of multi press.

        if kp == r'\xe0\x8a':
            # If user pressed the secret key, the game ends.
            # \x8a is CTRL+F12, that's the secret key.

            print(bk + "CONGRATULATIONS YOU PRESSED THE SECRET KEYS!\a" + bk)
            print("Press any key to exit the game")
            msvcrt.getch()
            break
        else:
            print("    You pressed:'", kp + "', that's not the secret key(s)\n")
            if self.select_continue() == "n":
                if self.secondary_options():
                    self._main_menu()
                break

Jeśli chcesz mieć pełny kod źródłowy porgramu, możesz go zobaczyć lub pobrać stąd:

The Secret Key Game (GitHub)

(uwaga: tajne naciśnięcie klawisza to: Ctrl+ F12)

Mam nadzieję, że możesz służyć jako przykład i pomóc tym, którzy przychodzą, aby zapoznać się z tymi informacjami.

Ferd
źródło
1
key = cv2.waitKey(1)

To pochodzi z pakietu openCV. Wykrywa naciśnięcie klawisza bez czekania.

eyllanesc
źródło