Sondeo del teclado (detectar una pulsación de tecla) en python

¿Cómo puedo sondear el teclado desde una aplicación de consola de python? Específicamente, me gustaría hacer algo similar a esto en medio de muchas otras actividades de E / S (selecciones de zócalo, acceso a puerto serie, etc.):

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 

¿Cuál es la forma correcta de pythonic para hacer esto en Windows? Además, la portabilidad a Linux no sería mala, aunque no es necesaria.

El enfoque estándar es utilizar el módulo de selección .

Sin embargo, esto no funciona en Windows. Para eso, puedes usar el sondeo del teclado del módulo msvcrt .

A menudo, esto se hace con varios subprocesos, uno por dispositivo que se “mira” más los procesos en segundo plano que el dispositivo puede necesitar interrumpir.

 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 

Una solución utilizando el módulo de curses. Imprimiendo un valor numérico correspondiente a cada tecla presionada:

 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) 

Ok, ya que mi bash de publicar mi solución en un comentario falló, esto es lo que estaba tratando de decir. Podría hacer exactamente lo que quería de Python nativo (en Windows, pero no en ningún otro lugar) con el siguiente código:

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

Ninguna de estas respuestas funcionó bien para mí. Este paquete, pynput, hace exactamente lo que necesito.

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() 

Puedes ver cómo Pygame maneja esto para robar algunas ideas.

De los comentarios:

 import msvcrt # built-in module def kbfunc(): return ord(msvcrt.getch()) if msvcrt.kbhit() else 0 

Gracias por la ayuda. Terminé escribiendo una DLL de C llamada PyKeyboardAccess.dll y accediendo a las funciones de crt conio, exportando esta rutina:

 #include  int kb_inkey () { int rc; int key; key = _kbhit(); if (key == 0) { rc = 0; } else { rc = _getch(); } return rc; } 

Y lo accedo en python usando el módulo ctypes (integrado en 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) 

Estoy usando esto para verificar si se presionan las teclas, no puedo ser mucho más simple:

 #!/usr/bin/python3 # -*- coding: UTF-8 -*- import curses, time def main(stdscr): """checking for keypress""" stdscr.nodelay(True) # do not wait for input when calling getch return stdscr.getch() while True: print("key:", curses.wrapper(main)) # prints: 'key: 97' for 'a' pressed # '-1' on no presses time.sleep(1) 

Mientras que curses no está funcionando en Windows, hay una versión ‘unicurses’, que supuestamente funciona en Linux, Windows, Mac pero no pude hacer que esto funcionara

Si combina time.sleep, threading.Thread y sys.stdin.read, puede esperar fácilmente un tiempo específico para la entrada y luego continuar, también debe ser compatible con varias plataformas.

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

También podría colocar esto en una función como tal

 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 

Aunque esto no devolverá nada, en lugar de eso, debe usar el módulo de grupo de multiprocesamiento que puede encontrar aquí: ¿cómo obtener el valor de retorno de un subproceso en python?

Me he encontrado con una implementación multiplataforma de kbhit en http://home.wlu.edu/~levys/software/kbhit.py (ediciones realizadas para eliminar el código irrelevante):

 import os if os.name == 'nt': import msvcrt else: import sys, select def kbhit(): ''' Returns True if a keypress is waiting to be read in stdin, False otherwise. ''' if os.name == 'nt': return msvcrt.kbhit() else: dr,dw,de = select.select([sys.stdin], [], [], 0) return dr != [] 

Asegúrate de read() el (los) carácter (es) en espera – la función seguirá regresando a True hasta que lo hagas!