2008-11-15 31 views
56

¿Cómo puedo sondear el teclado desde una aplicación de consola de python? En concreto, me gustaría hacer algo parecido a esto en medio de un montón de otras E/S (actividades selecciona socket, el acceso al puerto serie, etc.):Sondear el teclado (detectar una pulsación de tecla) en python

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 hacer Pythonic esto en Windows? Además, la portabilidad a Linux no sería mala, aunque no es necesaria.

+0

sólo para que otras personas sepan, he encontrado que la mayoría de las soluciones que implican las bibliotecas selectas o hilo no funcionan correctamente desde el ralentí. Sin embargo, _ ** todos ** _ funcionaron bien en la CLI, es decir, 'python/home/pi/poll_keyboard.py' – davidhood2

Respuesta

26

El enfoque estándar es utilizar el módulo select.

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

A menudo, esto se hace con múltiples hilos, uno por dispositivo que se "ve" más los procesos de fondo que pueden necesitar ser interrumpidos por el dispositivo.

6

Puede ver cómo pygame maneja esto para robar algunas ideas.

5

De los comentarios:

import msvcrt # built-in module 

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

Gracias por la ayuda. Terminé de escribir una DLL llamada C PyKeyboardAccess.dll y acceder a las funciones CRT Conio, exportando esta rutina:

#include <conio.h> 

int kb_inkey() { 
    int rc; 
    int key; 

    key = _kbhit(); 

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

    return rc; 
} 

Y puedo acceder a él en Python usando el módulo ctypes (construido 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) 
+0

¿Cómo es esto mejor que la función incorporada msvcrt.kbhit()? ¿Qué ventaja tiene? –

+0

¡Tienes toda la razón! Leí mal tu publicación; ¡No me di cuenta de que hay un módulo de Python llamado msvcrt! Pensé que querías decir "usa el ms crt", y luego me detuve a pensar en los hilos y no conecté los puntos. Tienes toda la razón. –

+1

me hizo lo mismo con: importación msvcrt def kbfunc(): x = msvcrt.kbhit() si x: ret = ord (msvcrt.getch()) otra cosa: ret = 0 return ret –

14

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

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

no funciona. error obtenido: select.error: (10093, 'O la aplicación no ha llamado WSAStartup, o WSAStartup failed') – DarenW

+1

He escuchado, más de un par de veces, que la llamada al sistema de selección en MS Windows no admite archivos regulares descriptores y solo funciona en sockets. (No sé si la implementación de Python de select() alguna vez ha funcionado bajo eso). –

+0

¿Para qué es este tiempo de espera extraño? Funciona para mí con timeout = 0 pero no con 0.0001 como se muestra .. – frans

13

Una solución utilizando el módulo de maldiciones. Impresión de un valor numérico correspondiente a cada tecla pulsada:

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

Esto no funciona en Windows ... – Oz123

+0

OZ123: Puede. Consulte http://stackoverflow.com/questions/32417379/what-is-needed-for-curses-in-python-3-4-on-windows7 –

+0

Tenía problemas al usar curses a través del término SSH en host sin cabeza. Los problemas estaban estropeando gravemente el terminal, lo que requería que se "reiniciara" entre cada ejecución. Funcionó, es decir, detecta la pulsación de tecla. Tiene que haber una solución más inteligente. – Mark

0

Si se combinan time.sleep, threading.Thread y sys.stdin.read puede esperar fácilmente por un período de tiempo especificado para la entrada y luego continuar, también esto debe ser compatible multiplataforma.

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

También puede 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 va a devolver nada por lo que su lugar debe usar el módulo de la piscina multiprocesamiento se puede encontrar aquí: how to get the return value from a thread in python?

+0

No debería ser esa primera línea: t = threading.Thread (target = sys.stdin.read, args = (1,)) –

+0

¿Esta solución no siempre duerme durante 5 segundos, incluso si el usuario presiona una tecla antes ¿ese? –

7

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

Esto funcionó para mí, excepto que la tecla presionada se hizo eco en la pantalla inmediatamente después de ser presionada, y no había forma de desactivarla. https://github.com/moses-palmer/pynput/issues/47 Además, los caracteres se almacenan en el búfer y, además, aparecen en la línea de comandos cuando el programa también sale. Esto parece ser una limitación de la implementación de Linux , pero funciona bien en Windows. – Trevor

+0

Esta solución no funciona cuando el script se ejecuta a través de ssh. Se dispara con el error: 'Xlib.error.DisplayNameError: Bad display name "".' –

+0

Como se mencionó anteriormente por David: esta no es una buena solución para las instancias sin cabeza ya que tiene una dependencia en Xserver. 'import Xlib.display' – Mark

Cuestiones relacionadas