detectar pulsación de tecla en Python?

103

Estoy haciendo un programa de tipo cronómetro en Python y me gustaría saber cómo detectar si se presiona una tecla (como p para pausa y s para detener), y no me gustaría que fuera algo como raw_input que espera el entrada del usuario antes de continuar con la ejecución. ¿Alguien sabe cómo hacer esto en un bucle while?

Además, me gustaría hacer esta plataforma multiplataforma, pero si eso no es posible, entonces mi principal objetivo de desarrollo es Linux.

lobuo
fuente
para OS X stackoverflow.com/a/47197390/5638869 funciona en Python 2 y 3
neoDev

Respuestas:

69

Python tiene un módulo de teclado con muchas funciones. Instálelo, quizás con este comando:

pip3 install keyboard

Luego úselo en un código como:

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
Comunidad
fuente
1
No estoy seguro para Linux, pero funciona en Windows para mí.
71
keyboardaparentemente requiere root en linux: /
Inaimathi
Probé esta solución, pero cuando intento importar el módulo después de instalarlo, aparece un "ImportError: Ningún módulo llamado 'teclado'", por lo que no funcionó. Revisé el repositorio de GitHub y encontré un problema relacionado , pero no me resuelve el problema. Luego, intenté descargar el repositorio y ejecutar algunos de sus ejemplos, pero obtengo un "ImportError: debes ser root para usar esta biblioteca en linux", como @Inaimathi comentó antes. Aparentemente parece un módulo completo para administrar el teclado con Python, pero el requisito de root es una gran falta :(
Ivanhercaz
3
"Para evitar depender de X, las partes de Linux leen archivos de dispositivo sin formato (/ dev / input / input *) pero esto requiere root".
jrouquie
7
No veo por qué el intento: excepto: sería útil.
TípicoHog
49

Para aquellos que están en Windows y estaban luchando por encontrar una respuesta que funcione, aquí está la mía: 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()

La función anterior imprimirá cualquier tecla que esté presionando e iniciará una acción cuando suelte la tecla 'esc'. La documentación del teclado está aquí para un uso más variado.

Markus von Broady destacó un problema potencial que es: Esta respuesta no requiere que esté en la ventana actual para activar este script, una solución para Windows sería:

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
fuente
7
@ nimig18 ... y no requiere root :)
cz
1
Hay un problema con esta solución (no estoy seguro de las alternativas): no es necesario presionar la tecla dentro de una ventana de la consola para que surta efecto. Imagínese tener un script que hace algún trabajo hasta que se presiona ESC, pero luego lo presiona en otro programa.
Markus von Broady
1
@MarkusvonBroady Supongo que win32gui sería suficiente para resolverlo, he editado mi respuesta de una manera que potencialmente lo resolvería al menos para los usuarios de Windows.
Mitrek
@Mitrek Intenté esto, pero mi código detiene la ejecución adicional y está atascado aquí. Funciona como input (). Tengo el código ejecutándose en selenium, firefox, pero tan pronto como se encuentra esta secuencia, no hay más acciones.
Lakshmi Narayanan
1
Debería haber sido la respuesta aceptada, ya que funciona tanto en Linux como en Windows
Akash Karnatak
31

Como OP menciona sobre raw_input, eso significa que quiere una solución cli. Linux: curses es lo que quieres (Windows PDCurses). Curses, es una API gráfica para software cli, puede lograr más que solo detectar eventos clave.

Este código detectará teclas hasta que se presione una nueva línea.

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
fuente
Esto es realmente bueno. Tuve que buscar una eternidad antes de encontrarlo. Parece mucho más limpio que hackear con termiosy así sucesivamente ...
Hugh Perkins
5
necesario agregar import ospara poder salir, salir del ejemplo.
Malta
Si lo hace en win.nodelay(False)lugar de True, no generará un millón de excepciones por segundo.
Johannes Hoff
24

Hay más cosas que se pueden hacer con keyboardmodule.

Estos son algunos de los métodos:


Método 1:

Usando la función read_key():

import keyboard

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

Esto romperá el ciclo cuando pse presione la tecla .


Método # 2:

Función de uso wait:

import keyboard

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

Esperará a que presione py continúe el código mientras se presiona.


Método # 3:

Usando la función on_press_key:

import keyboard

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

Necesita una función de devolución de llamada. Lo usé _porque la función del teclado devuelve el evento del teclado a esa función.

Una vez ejecutada, ejecutará la función cuando se presione la tecla. Puede detener todos los ganchos ejecutando esta línea:

keyboard.unhook_all()

Método # 4:

Este método ya ha sido respondido por user8167727 pero no estoy de acuerdo con el código que hicieron. Estará usando la función is_pressedpero de otra manera:

import keyboard

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

Romperá el bucle cuando pse presione.


Notas:

  • keyboard leerá las pulsaciones de teclas de todo el sistema operativo.
  • keyboard requiere root en linux
Trueno Negro
fuente
11
El mayor NEGATIVO de usar el módulo de teclado es su requisito de ejecutar como usuario ROOT. Esto hace que el módulo esté prohibido en mi código. Solo para sondear si se ha presionado una tecla no se requieren privilegios de root. He leído el documento y entiendo por qué la limitación existe en el módulo, pero busque en otro lado si todo lo que necesita es sondear una clave ...
muman
¡Se ha compartido información muy útil, señor! Quería saber si puedo keyboard.wait()esperar más de una tecla y continuar si se presiona alguna de ellas
Preetkaran Singh
@PreetkaranSingh wait()no ofrece esta funcionalidad. Tendrá que usarlo keyboard.read_key()con una condición if empaquetada en un bucle while. Vea el método # 1
Black Thunder
¡Gracias señor !, ¿le gustaría arrojar algo de luz sobre el suppressuso de palabras clave en keyboard.read_key(), cuándo usarlas y cuándo no ...?
Preetkaran Singh
@PreetkaranSingh Lo haría, pero no tengo suficiente información sobre el argumento de supresión
Black Thunder
13

Para Windows se puede utilizar msvcrtasí:

   import msvcrt
   while True:
       if msvcrt.kbhit():
           key = msvcrt.getch()
           print(key)   # just to show the result
Benjie
fuente
7
msvcrt es un módulo exclusivo de Windows.
Dunatotatos
1
De hecho, ahora uso pynput, esa podría ser una mejor respuesta
Benjie
Tenga en cuenta que pynput para trabajar en OS X (no sé sobre Linux) tiene que ejecutarse como root para que funcione. Eso puede no ser un comienzo para algunas personas.
Gabe Weiss
Podría haber jurado que la pregunta era para 'multiplataforma' o 'linux' ...
Aaron Mann
10

Use este código para encontrar la tecla presionada

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
fuente
Sin embargo, aquí está la cosa, estoy usando macOS e instalé tanto pynput como el teclado por separado, y el programa se ejecuta sin ningún error, pero solo puede detectar (en el shell de Python) teclas especiales. Las claves alfanuméricas no se detectan y por el contrario, se consideran como si estuviera escribiendo código en el shell. ¿Sabes cuál podría ser el problema?
Dario Deniz Ergün
El mismo código funcionó para mí en el shell. Compruébelo por favor. El paquete de teclado no necesita este código.
Manivannan Murugavel
1
Este es el camino a seguir en Linux, ya que la biblioteca del teclado necesita root.
David
1
Esta solución detectará todas las pulsaciones de teclas; también los que suceden en una ventana de terminal diferente. Desafortunadamente, esto limita severamente sus posibles casos de uso.
Serge Stroobandt
6

Use PyGame para tener una ventana y luego puede obtener los eventos clave.

Para la carta 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
fuente
2

Así que hice este ... tipo de juego ... basado en esta publicación (usando la biblioteca msvcr y Python 3.7).

La siguiente es la "función principal" del juego, que es detectar las teclas presionadas:

# 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

Si quieres el código fuente completo del programa puedes verlo o descargarlo desde aquí:

El juego de la clave secreta (GitHub)

(nota: la pulsación de tecla secreta es: Ctrl+ F12)

Espero que pueda servir de ejemplo y ayuda para quienes vengan a consultar esta información.

Ferd
fuente
1
key = cv2.waitKey(1)

Esto es del paquete openCV. Detecta una pulsación de tecla sin esperar.

eyllanesc
fuente