Comment interroger le clavier depuis une application python de la console? Plus précisément, j'aimerais faire quelque chose de similaire avec beaucoup d'autres activités d'E/S (sélection de socket, accès au port série, 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
Quelle est la bonne façon de faire cela sous Pythonic sous Windows? En outre, la portabilité vers Linux ne serait pas mauvaise, bien que ce ne soit pas obligatoire.
L'approche standard consiste à utiliser le module select .
Cependant, cela ne fonctionne pas sous Windows. Pour cela, vous pouvez utiliser l'interrogation au clavier du module msvcrt .
Cela se fait souvent avec plusieurs threads - un par périphérique "surveillé", plus les processus d'arrière-plan qui pourraient devoir être interrompus par le périphérique.
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
Ok, puisque ma tentative d’afficher ma solution dans un commentaire a échoué, voici ce que j’essayais de dire. Je pouvais faire exactement ce que je voulais à partir de Python natif (sous Windows, pas ailleurs) avec le code suivant:
import msvcrt
def kbfunc():
x = msvcrt.kbhit()
if x:
ret = ord(msvcrt.getch())
else:
ret = 0
return ret
Une solution utilisant le module curses. Imprimer une valeur numérique correspondant à chaque touche enfoncée:
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 __== '__main__':
curses.wrapper(main)
Aucune de ces réponses n'a bien fonctionné pour moi. Ce paquetage, pynput, fait exactement ce dont j'ai besoin.
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()
Vous pouvez regarder comment pygame gère ceci pour voler quelques idées.
D'après les commentaires:
import msvcrt # built-in module
def kbfunc():
return ord(msvcrt.getch()) if msvcrt.kbhit() else 0
Merci pour l'aide. J'ai fini par écrire un C DLL appelé PyKeyboardAccess.dll et accéder aux fonctions crt conio en exportant cette routine:
#include <conio.h>
int kb_inkey () {
int rc;
int key;
key = _kbhit();
if (key == 0) {
rc = 0;
} else {
rc = _getch();
}
return rc;
}
Et j'y accède en python en utilisant le module ctypes (intégré à 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)
J'utilise ceci pour vérifier si vous appuyez sur une touche, je ne peux pas être beaucoup plus 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)
Alors que curses ne fonctionne pas sous Windows, il existe une version 'unicurses', supposée fonctionner sous Linux, Windows, Mac mais je ne pouvais pas que cela fonctionne.
Si vous combinez time.sleep, threading.Thread et sys.stdin.read, vous pouvez facilement attendre un laps de temps spécifié avant de continuer.
t = threading.Thread(target=sys.stdin.read(1) args=(1,))
t.start()
time.sleep(5)
t.join()
Vous pouvez également placer cela dans une fonction comme
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
Bien que cela ne retourne rien, vous devriez plutôt utiliser le module de pool de multitraitement que vous pouvez trouver ici: comment obtenir la valeur de retour d'un thread en python?
J'ai rencontré une implémentation multi-plateforme de kbhit
à http://home.wlu.edu/~levys/software/kbhit.py (modifications apportées pour supprimer le code non pertinent):
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 != []
Assurez-vous de read()
le (s) caractère (s) en attente - la fonction retournera True
jusqu'à ce que vous le fassiez!