Cómo Detectar la Pulsación de una Tecla en Python

Cómo Detectar la Pulsación de una Tecla en Python

Existen muchos motivos para querer detectar y capturar la pulsación de una tecla en Python, por ejemplo, para hacer que nuestro programa ejecute alguna operación o comando, para interrumpir un proceso, para realizar algún control, etc. En cualquier caso, resolver esta tarea a veces no es fácil ni directo o sencillo, pero aquí te voy a dar unas cuantas claves para que lo logres con éxito.

Se puede detectar la pulsación de una tecla en Python, mediante la librería pynput, lanzando un listener y asociando una función f al evento de tecla presionada: keyboard.Listener(f).run(). Cuando se pulse el teclado se invocará la función f que recibirá por parámetro la tecla presionada.

Esta solución, aunque sencilla, es con bloqueo, es decir, el programa se queda en espera. Si necesitas una solución que no imponga bloqueo, te doy a continuación algunas variantes con diversos matices para lograr exactamente el comportamiento que quieres. Si lo deseas, aunque está en inglés, puedes consultar la documentación oficial de la librería pynput.

Detectando y capturando la pulsación y la liberación de una tecla

Aunque existen otras alternativas, que te contaré más adelante, la verdad es que la librería pynput es la que me ha resultado más fácil de usar, más directa y más flexible. Se basa en el uso de hilos o threads, cosa que implica que su ejecución se realice con bloqueo, es decir, que haga que tu programa se quede en espera, o sin bloqueo.

A continuación te voy a contar un poco las alternativas que tienes para detectar la pulsación de cualquier tecla, así como su liberación y poder conocer de qué tecla se trata para actuar en consecuencia.

Detección de tecla con llamada con bloqueo

Antes de comenzar recuerda instalar la librería mediante la llamada pip install pynput.

La librería pynput proporciona funcionalidades de control del teclado y del ratón. En este caso, como lo que nos interesa es el teclado, nos centraremos en el módulo keyboard. Dicho módulo permite lanzar un listener o escuchador, que es un hilo de ejecución que está monitorizando el teclado.

Cuando el escuchador detecta que se presiona o se suelta una tecla nos lo comunica invocando una función que hayamos definido.

Por ejemplo, vamos a definir una función pulsa que escriba por pantalla el nombre de la tecla presionada cada vez que se detecte una. Además, crearemos el escuchador (clase Listener) al que asociaremos la función pulsa. Después ejecutaremos el escuchador, que bloqueará la ejecución del código, detectará las teclas e invocará a nuestra función:

from pynput import keyboard as kb

def pulsa(tecla):
	print('Se ha pulsado la tecla ' + str(tecla))

with kb.Listener(pulsa) as escuchador:
	escuchador.join()

Así de sencillo. Mientras el programa esté en ejecución, si pulsamos alguna tecla se ejecutará la función pulsa mostrando el mensaje correspondiente por pantalla. Por ejemplo, si pulsas la tecla ‘a’ después la tecla ‘espacio’ y después la tecla ‘control’ derecha, obtendrás lo siguiente por pantalla:

Se ha pulsado la tecla 'a'
Se ha pulsado la tecla Key.space
Se ha pulsado la tecla Key.ctrl_r

De la misma manera, puedes crear una función suelta que se ejecute cada vez que se libera una tecla y asociar dicha función al escuchador también, de la siguiente forma:

from pynput import keyboard as kb

def pulsa(tecla):
	print('Se ha pulsado la tecla ' + str(tecla))

def suelta(tecla):
	print('Se ha soltado la tecla ' + str(tecla))

with kb.Listener(pulsa, suelta) as escuchador:
	escuchador.join()

Cabe mencionar que, como es evidente, puedes definir tú las funciones que necesitas para asociar al escuchador y colocar en ellas el código que tú quieras.

El código hace que el programa se quede escuchando «para siempre» hasta que lo detengamos. La manera más sencilla de hacerlo es devolviendo False en una de las funciones asociadas al escuchador.

Otra alternativa similar, incluyendo esta vez un código para detener la ejecución si se presiona la tecla ‘q’ es con la llamada a run como indicaba al principio del artículo:

from pynput import keyboard as kb

def pulsa(tecla):
	print('Se ha pulsado la tecla ' + str(tecla))

def suelta(tecla):
	print('Se ha soltado la tecla ' + str(tecla))
	if tecla == kb.KeyCode.from_char('q'):
		return False

kb.Listener(pulsa, suelta).run()

Veamos ahora cómo hacer lo mismo pero con una ejecución sin bloqueo.

Detección de tecla con llamada sin bloqueo

Si necesitas lanzar el escuchador pero que no se bloquee la ejecución, por ejemplo porque estás ejecutando un interfaz gráfico de usuario, puedes sustituir la llamada a run por una a start que se encarga de ejecutar el escuchador pero en un hilo diferente.

Debes tener cuidado si ejecutas el escuchador en un hilo diferente, pues si el hilo principal termina, terminará tu programa sin que puedas detectar las teclas.

Para simular la ejecución de otro código en el hilo principal, haré uso de un bucle que se ejecute mientras el hilo del escuchado también se esté ejecutando, utilizando para ello la función is_alive, forzando así que el programa principal quede en espera hasta que se pulsa la tecla ‘q’:

from pynput import keyboard as kb

def pulsa(tecla):
	print('Se ha pulsado la tecla ' + str(tecla))

def suelta(tecla):
	print('Se ha soltado la tecla ' + str(tecla))
	if tecla == kb.KeyCode.from_char('q'):
		return False

escuchador = kb.Listener(pulsa, suelta)
escuchador.start()

while escuchador.is_alive():
	pass

De esta manera, y sustituyendo el bucle por lo que necesites, puedes capturar las pulsaciones del teclado en un hilo separado al principal.

Detección de hotkeys o combinaciones de teclas

Es muy frecuente que queramos detectar una combinación de teclas del estilo Alt + F4 o Ctrl + Alt + Del. Como los escuchadores no mantienen estado necesitamos almacenar nosotros el estado de qué teclas están siendo presionadas en un momento dado. Para esto, pynput nos proporciona la clase pynput.keyboard.HotKey, con dos métodos, press y release, que se pueden asociar directamente al escuchador o ser invocados directamente en las funciones asociadas a este.

Para la combinación de teclas que quieras detectar debes crear un objeto de clase HotKey y asociarle una función con el código que quieras ejecutar.

Hay que tener en cuenta que, a la hora de detectar combinaciones de teclas, es necesario realizar una normalización. Esto es debido a que la detección debe llevarse a cabo con independencia de si una letra pulsada está en mayúsculas o minúsculas o de la tecla física asociada a una función.

Por ejemplo, si queremos detectar la combinación Ctrl + C, tiene que ser válido tanto pulsar la tecla Ctrl izquierda y la c como pulsar la tecla Ctrl derecha y la C con las mayúsculas activadas. Para esto es necesario invocar a la función canonical que se encarga de esta tarea, además de gestionar los estados de las teclas junto a HotKey. Veamos un ejemplo completo con todos estos elementos para la detección de la combinación de teclas Ctrl + q:

from pynput import keyboard as kb

#definimos las funciones que se asociarán al escuchador
#las funciones deben invocar a press y release
def pulsa(teclas):
	hotkey.press(escuchador.canonical(teclas))

def suelta(teclas):
	hotkey.release(escuchador.canonical(teclas))

#definimos la función que se ejecutará al detectar la combinación
def pulsa_ctrl_q():
	print('Se ha pulsado Ctrl + q')

#definimos la combinación de teclas que queremos detectar
ctrl_q = kb.HotKey.parse('<ctrl>+q')

#indicamos qué función se ejecutará al detectarla
hotkey = kb.HotKey(ctrl_q, pulsa_ctrl_q)

#creamos y lanzamos el escuchador
with kb.Listener(pulsa, suelta) as escuchador:
	escuchador.join()

Tal y como está implementado este ejemplo, puedes añadir, además, el código que necesites cuando se detecta la pulsación o la liberación de cualquier tecla de manera individual en los métodos pulsa y suelta. Pero si no necesitas hacer nada a mayores, puedes compactar el código de esta manera:

from pynput import keyboard as kb

#esta función devuelve otra que se encarga de llamar a canonical
def para_canonical(f):
	return lambda teclas: f(escuchador.canonical(teclas))

#definimos la función que se ejecutará al detectar la combinación
def pulsa_ctrl_q():
	print('Se ha pulsado Ctrl + q')

#definimos la combinación de teclas que queremos detectar
ctrl_q = kb.HotKey.parse('<ctrl>+q')

#indicamos qué función se ejecutará al detectarla
hotkey = kb.HotKey(ctrl_q, pulsa_ctrl_q)

#creamos y lanzamos el escuchador
with kb.Listener(para_canonical(hotkey.press), para_canonical(hotkey.release)) as escuchador:
	escuchador.join()

Finalmente, si lo que necesitas es detectar varias combinaciones y no solo una, puedes hacer uso de la clase pynput.keyboard.GlobalHotKey que se encarga de lanzar un escuchador para un conjunto de combinaciones de teclas. Puedes usarlo de la siguiente manera:

from pynput import keyboard

#definimos las funciones que se ejecutarán en la detección
def pulsa_ctrl_q():
    print('Se ha pulsado <ctrl>+q')

def pulsa_alt_c():
    print('Se ha pulsado <alt>+c')

def pulsa_ctrl_alt_s():
    print('Se ha pulsado <ctrl>+<alt>+s')
	 
#definimos un diccionario con cada combinación de teclas y la función asociada
hotkeys = { '<ctrl>+q': pulsa_ctrl_q,
				'<alt>+c': pulsa_alt_c,
				'<ctrl>+<alt>+s': pulsa_ctrl_alt_s }

#finalmente lanzamos el escuchador con la clase GlobalHotKeys
with keyboard.GlobalHotKeys(hotkeys) as escuchador:
    escuchador.join()

Como te habrás dado cuenta, en este caso no hace falta invocar a canonical porque ya lo hace GlobalHotKeys por nosotros. Muy cómodo.

Limitaciones de pynput, problemas, advertencias y alternativas

Ten en cuenta que en Linux la librería pynput usar el servidor gráfico X para poder funcionar de forma que es necesario que se esté ejecutando y que la variable de entorno $DISPLAY tenga un valor válido.

En Windows es posible que no se puedan detectar eventos virtuales enviados por otros procesos.

En macOS, en concreto en las últimas versiones, hay implementadas restricciones de seguridad que impiden monitorizar el teclado (por ejemplo para que no se pueda instalar en el equipo un keylogger que se haga con nuestras contraseñas). Por tanto, para que lo contado en este artículo funcione en un Mac tienes la alternativa de ejecutarlo como root, o meter tu programa en la lista blanca, para lo cual tienes que empaquetarlo como una aplicación. También puedes incluir toda la instalación de Python en la lista blanca, así como la aplicación de terminal si ejecutas tu aplicación en él.

Si no puedes convivir con estas limitaciones o necesitas abordar el problema desde otro enfoque te propongo tres librerías alternativas que puedes explorar:

  • El módulo keyboard. Similar a pynput, funciona tanto en Windows como en Linux, aunque con la desventaja de que en Linux hay que ser root para que funcione. Para Mac el soporte es experimental todavía.
  • Curses es una librería de C para el desarrollo de aplicaciones con interfaz en línea de comandos. Entre otras muchas cosas permite la detección de teclas. Para su uso en Python con Linux o Mac puedes utilizar la librería curses que ya viene incluida con la instalación de Python. En Windows puede que necesites instalar el paquete windows-curses.
  • El paquete tkinter es una interfaz de la librería Tk para el desarrollo de interfaces gráficas de usuario que está disponible en Linux, Mac y Windows. Al igual que sucede con Curses, esta librería permite la detección de eventos de teclado, por lo que también puede ser usada para tu propósito.

¿He solucionado tu problema? ¡Espero que sí!

La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
Consigue trucos, consejos y actualizaciones y, por supuesto, la Hoja de Referencia de Python gratis.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Laura Otero Moreira. Finalidad de la recogida y tratamiento de los datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.