web-dev-qa-db-fra.com

Quel est le moyen le plus simple de détecter une entrée au clavier dans python à partir du terminal?

J'ai un simple python), qui comporte certaines fonctions qui s'exécutent en boucle (je prends des lectures de capteurs).

while True:
    print "Doing a function"

Si le clavier est enfoncé, j'aimerais imprimer "touche enfoncée".

Quel est le moyen le plus simple de faire cela en Python? J'ai cherché haut et bas. J'ai découvert comment faire avec pygame, mais je préférerais le faire sans. Si je dois utiliser pygame, est-il possible de ne pas avoir de fenêtre séparée pour l'application ?:

import pygame, time
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption('Pygame Keyboard Test')
pygame.mouse.set_visible(0)


while True:

   print "doing a function"

    for event in pygame.event.get():
      if (event.type == KEYUP) or (event.type == KEYDOWN):
         print "key pressed"
         time.sleep(0.1)
43
Tom R

Modifier:

J'ai beaucoup réfléchi à ce problème et il y a quelques comportements différents que l'on pourrait souhaiter. J'en ai implémenté la plupart pour Unix et Windows, et les posterai ici une fois qu'ils seront terminés.

Capture de clé synchrone/bloquante:

  1. Un simple input ou raw_input, une fonction de blocage qui renvoie le texte tapé par un utilisateur lorsqu'il appuie sur une nouvelle ligne.
  2. Une simple fonction de blocage qui attend que l'utilisateur appuie sur une seule touche, puis renvoie cette clé

Capture de clé asynchrone:

  1. Un rappel qui est appelé avec la touche enfoncée chaque fois que l'utilisateur tape une clé dans l'invite de commande, même lors de la saisie d'éléments dans un interpréteur (un enregistreur de frappe)
  2. Un rappel qui est appelé avec le texte tapé après que l'utilisateur a appuyé sur Entrée (un enregistreur de frappe moins réel)
  3. Un rappel appelé avec les touches enfoncées lorsqu'un programme est en cours d'exécution (par exemple, dans une boucle for ou une boucle while)

Vote:

  1. L'utilisateur veut simplement pouvoir faire quelque chose quand une touche est enfoncée, sans avoir à attendre cette touche (donc cela devrait être non bloquant). Ainsi, ils appellent une fonction poll () et cela retourne une clé ou aucun. Cela peut être avec perte (s'ils prennent trop de temps entre les sondages, ils peuvent rater une clé) ou non (le poller stockera l'historique de toutes les touches enfoncées. Ainsi, lorsque la fonction poll () les demandera, elles seront toujours renvoyées. dans l'ordre pressé).

  2. Identique à 1 sauf que le sondage ne renvoie quelque chose que lorsque l'utilisateur appuie sur une nouvelle ligne.

Robots:

Celles-ci peuvent être appelées pour déclencher par programme des événements de clavier. Ceci peut être utilisé à côté des captures de clé pour les renvoyer à l'utilisateur

Implémentations

Capture de clé synchrone/bloquante:

Un simple input ou raw_input, une fonction de blocage qui renvoie le texte tapé par un utilisateur lorsqu'il appuie sur une nouvelle ligne.

typedString = raw_input()

Une simple fonction de blocage qui attend que l'utilisateur appuie sur une seule touche, puis renvoie cette clé

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen. From http://code.activestate.com/recipes/134892/"""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            try:
                self.impl = _GetchMacCarbon()
            except(AttributeError, ImportError):
                self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()

class _GetchMacCarbon:
    """
    A function which returns the current ASCII key that is down;
    if no ASCII key is down, the null string is returned.  The
    page http://www.mactech.com/Macintosh-c/chap02-1.html was
    very helpful in figuring out how to do this.
    """
    def __init__(self):
        import Carbon
        Carbon.Evt #see if it has this (in Unix, it doesn't)

    def __call__(self):
        import Carbon
        if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
            return ''
        else:
            #
            # The event contains the following info:
            # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
            #
            # The message (msg) contains the ASCII char which is
            # extracted with the 0x000000FF charCodeMask; this
            # number is converted to an ASCII character with chr() and
            # returned
            #
            (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
            return chr(msg & 0x000000FF)


def getKey():
    inkey = _Getch()
    import sys
    for i in xrange(sys.maxint):
        k=inkey()
        if k<>'':break

    return k

Capture de clé asynchrone:

Un rappel qui est appelé avec la touche enfoncée chaque fois que l'utilisateur tape une clé dans l'invite de commande, même lors de la saisie d'éléments dans un interpréteur (un enregistreur de frappe)

Un rappel qui est appelé avec le texte tapé après que l'utilisateur a appuyé sur Entrée (un enregistreur de frappe moins réel)

Les fenêtres:

Ceci utilise le robot Windows donné ci-dessous, en nommant le script keyPress.py

# Some if this is from http://nullege.com/codes/show/src@e@i@einstein-HEAD@Python25Einstein@[email protected]/380/win32api.GetStdHandle
# and
# http://nullege.com/codes/show/src@v@i@Vista-HEAD@Python@[email protected]/901/win32console.GetStdHandle.PeekConsoleInput

from ctypes import *
import time
import threading

from win32api import STD_INPUT_HANDLE, STD_OUTPUT_HANDLE

from win32console import GetStdHandle, KEY_EVENT, ENABLE_WINDOW_INPUT, ENABLE_MOUSE_INPUT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT

import keyPress


class CaptureLines():
    def __init__(self):
        self.stopLock = threading.Lock()

        self.isCapturingInputLines = False

        self.inputLinesHookCallback = CFUNCTYPE(c_int)(self.inputLinesHook)
        self.pyosInputHookPointer = c_void_p.in_dll(pythonapi, "PyOS_InputHook")
        self.originalPyOsInputHookPointerValue = self.pyosInputHookPointer.value

        self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

    def inputLinesHook(self):

        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)
        inputChars = self.readHandle.ReadConsole(10000000)
        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT)

        if inputChars == "\r\n":
            keyPress.KeyPress("\n")
            return 0

        inputChars = inputChars[:-2]

        inputChars += "\n"

        for c in inputChars:
            keyPress.KeyPress(c)

        self.inputCallback(inputChars)

        return 0


    def startCapture(self, inputCallback):
        self.stopLock.acquire()

        try:
            if self.isCapturingInputLines:
                raise Exception("Already capturing keystrokes")

            self.isCapturingInputLines = True
            self.inputCallback = inputCallback

            self.pyosInputHookPointer.value = cast(self.inputLinesHookCallback, c_void_p).value
        except Exception as e:
            self.stopLock.release()
            raise

        self.stopLock.release()

    def stopCapture(self):
        self.stopLock.acquire()

        try:
            if not self.isCapturingInputLines:
                raise Exception("Keystrokes already aren't being captured")

            self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

            self.isCapturingInputLines = False
            self.pyosInputHookPointer.value = self.originalPyOsInputHookPointerValue

        except Exception as e:
            self.stopLock.release()
            raise

        self.stopLock.release()

Un rappel appelé avec les touches enfoncées lorsqu'un programme est en cours d'exécution (par exemple, dans une boucle for ou une boucle while)

Les fenêtres:

import threading
from win32api import STD_INPUT_HANDLE
from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT


class KeyAsyncReader():
    def __init__(self):
        self.stopLock = threading.Lock()
        self.stopped = True
        self.capturedChars = ""

        self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)



    def startReading(self, readCallback):
        self.stopLock.acquire()

        try:
            if not self.stopped:
                raise Exception("Capture is already going")

            self.stopped = False
            self.readCallback = readCallback

            backgroundCaptureThread = threading.Thread(target=self.backgroundThreadReading)
            backgroundCaptureThread.daemon = True
            backgroundCaptureThread.start()
        except:
            self.stopLock.release()
            raise

        self.stopLock.release()


    def backgroundThreadReading(self):
        curEventLength = 0
        curKeysLength = 0
        while True:
            eventsPeek = self.readHandle.PeekConsoleInput(10000)

            self.stopLock.acquire()
            if self.stopped:
                self.stopLock.release()
                return
            self.stopLock.release()


            if len(eventsPeek) == 0:
                continue

            if not len(eventsPeek) == curEventLength:
                if self.getCharsFromEvents(eventsPeek[curEventLength:]):
                    self.stopLock.acquire()
                    self.stopped = True
                    self.stopLock.release()
                    break

                curEventLength = len(eventsPeek)



    def getCharsFromEvents(self, eventsPeek):
        callbackReturnedTrue = False
        for curEvent in eventsPeek:
            if curEvent.EventType == KEY_EVENT:
                    if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
                        pass
                    else:
                        curChar = str(curEvent.Char)
                        if self.readCallback(curChar) == True:
                            callbackReturnedTrue = True


        return callbackReturnedTrue

    def stopReading(self):
        self.stopLock.acquire()
        self.stopped = True
        self.stopLock.release()

Vote:

L'utilisateur veut simplement pouvoir faire quelque chose quand une touche est enfoncée, sans avoir à attendre cette touche (donc cela devrait être non bloquant). Ainsi, ils appellent une fonction poll () et cela retourne une clé ou aucun. Cela peut être avec perte (s'ils prennent trop de temps entre les sondages, ils peuvent rater une clé) ou non (le poller stockera l'historique de toutes les touches enfoncées. Ainsi, lorsque la fonction poll () les demandera, elles seront toujours renvoyées. dans l'ordre pressé).

Windows et OS X (et peut-être Linux):

global isWindows

isWindows = False
try:
    from win32api import STD_INPUT_HANDLE
    from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT
    isWindows = True
except ImportError as e:
    import sys
    import select
    import termios


class KeyPoller():
    def __enter__(self):
        global isWindows
        if isWindows:
            self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
            self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

            self.curEventLength = 0
            self.curKeysLength = 0

            self.capturedChars = []
        else:
            # Save the terminal settings
            self.fd = sys.stdin.fileno()
            self.new_term = termios.tcgetattr(self.fd)
            self.old_term = termios.tcgetattr(self.fd)

            # New terminal setting unbuffered
            self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)

        return self

    def __exit__(self, type, value, traceback):
        if isWindows:
            pass
        else:
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)

    def poll(self):
        if isWindows:
            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)

            eventsPeek = self.readHandle.PeekConsoleInput(10000)

            if len(eventsPeek) == 0:
                return None

            if not len(eventsPeek) == self.curEventLength:
                for curEvent in eventsPeek[self.curEventLength:]:
                    if curEvent.EventType == KEY_EVENT:
                        if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
                            pass
                        else:
                            curChar = str(curEvent.Char)
                            self.capturedChars.append(curChar)
                self.curEventLength = len(eventsPeek)

            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)
            else:
                return None
        else:
            dr,dw,de = select.select([sys.stdin], [], [], 0)
            if not dr == []:
                return sys.stdin.read(1)
            return None

Cas d'utilisation simple:

with KeyPoller() as keyPoller:
    while True:
        c = keyPoller.poll()
        if not c is None:
            if c == "c":
                break
            print c

Idem que ci-dessus, sauf que poll ne renvoie quelque chose que lorsque l'utilisateur appuie sur une nouvelle ligne.

Robots:

Celles-ci peuvent être appelées pour déclencher par programme des événements de clavier. Ceci peut être utilisé à côté des captures de clé pour les renvoyer à l'utilisateur

Les fenêtres:

# Modified from http://stackoverflow.com/a/13615802/2924421

import ctypes
from ctypes import wintypes
import time

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE    = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP       = 0x0002
KEYEVENTF_UNICODE     = 0x0004
KEYEVENTF_SCANCODE    = 0x0008

MAPVK_VK_TO_VSC = 0

# C struct definitions
wintypes.ULONG_PTR = wintypes.WPARAM

SendInput = ctypes.windll.user32.SendInput

PUL = ctypes.POINTER(ctypes.c_ulong)

class KEYBDINPUT(ctypes.Structure):
    _fields_ = (("wVk",         wintypes.Word),
                ("wScan",       wintypes.Word),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class MOUSEINPUT(ctypes.Structure):
    _fields_ = (("dx",          wintypes.LONG),
                ("dy",          wintypes.LONG),
                ("mouseData",   wintypes.DWORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = (("uMsg",    wintypes.DWORD),
                ("wParamL", wintypes.Word),
                ("wParamH", wintypes.Word))

class INPUT(ctypes.Structure):
    class _INPUT(ctypes.Union):
        _fields_ = (("ki", KEYBDINPUT),
                    ("mi", MOUSEINPUT),
                    ("hi", HARDWAREINPUT))
    _anonymous_ = ("_input",)
    _fields_ = (("type",   wintypes.DWORD),
                ("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
    if result == 0:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
                             LPINPUT,       # pInputs
                             ctypes.c_int)  # cbSize

def KeyDown(unicodeKey):
    key, unikey, uniflag = GetKeyCode(unicodeKey)
    x = INPUT( type=INPUT_KEYBOARD, ki= KEYBDINPUT( key, unikey, uniflag, 0))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def KeyUp(unicodeKey):
    key, unikey, uniflag = GetKeyCode(unicodeKey)
    extra = ctypes.c_ulong(0)
    x = INPUT( type=INPUT_KEYBOARD, ki= KEYBDINPUT( key, unikey, uniflag | KEYEVENTF_KEYUP, 0))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def KeyPress(unicodeKey):
    time.sleep(0.0001)
    KeyDown(unicodeKey)
    time.sleep(0.0001)
    KeyUp(unicodeKey)
    time.sleep(0.0001)


def GetKeyCode(unicodeKey):
    k = unicodeKey
    curKeyCode = 0
    if k == "up": curKeyCode = 0x26
    Elif k == "down": curKeyCode = 0x28
    Elif k == "left": curKeyCode = 0x25
    Elif k == "right": curKeyCode = 0x27
    Elif k == "home": curKeyCode = 0x24
    Elif k == "end": curKeyCode = 0x23
    Elif k == "insert": curKeyCode = 0x2D
    Elif k == "pgup": curKeyCode = 0x21
    Elif k == "pgdn": curKeyCode = 0x22
    Elif k == "delete": curKeyCode = 0x2E
    Elif k == "\n": curKeyCode = 0x0D

    if curKeyCode == 0:
        return 0, int(unicodeKey.encode("hex"), 16), KEYEVENTF_UNICODE
    else:
        return curKeyCode, 0, 0

OS X:

#!/usr/bin/env python

import time
from Quartz.CoreGraphics import CGEventCreateKeyboardEvent
from Quartz.CoreGraphics import CGEventPost

# Python releases things automatically, using CFRelease will result in a scary error
#from Quartz.CoreGraphics import CFRelease

from Quartz.CoreGraphics import kCGHIDEventTap

# From http://stackoverflow.com/questions/281133/controlling-the-mouse-from-python-in-os-x
# and from https://developer.Apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//Apple_ref/c/func/CGEventCreateKeyboardEvent


def KeyDown(k):
    keyCode, shiftKey = toKeyCode(k)

    time.sleep(0.0001)

    if shiftKey:
        CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, 0x38, True))
        time.sleep(0.0001)

    CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, keyCode, True))
    time.sleep(0.0001)

    if shiftKey:
        CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, 0x38, False))
        time.sleep(0.0001)

def KeyUp(k):
    keyCode, shiftKey = toKeyCode(k)

    time.sleep(0.0001)

    CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, keyCode, False))
    time.sleep(0.0001)

def KeyPress(k):
    keyCode, shiftKey = toKeyCode(k)

    time.sleep(0.0001)

    if shiftKey:
        CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, 0x38, True))
        time.sleep(0.0001)

    CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, keyCode, True))
    time.sleep(0.0001)

    CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, keyCode, False))
    time.sleep(0.0001)

    if shiftKey:
        CGEventPost(kCGHIDEventTap, CGEventCreateKeyboardEvent(None, 0x38, False))
        time.sleep(0.0001)



# From http://stackoverflow.com/questions/3202629/where-can-i-find-a-list-of-mac-virtual-key-codes

def toKeyCode(c):
    shiftKey = False
    # Letter
    if c.isalpha():
        if not c.islower():
            shiftKey = True
            c = c.lower()

    if c in shiftChars:
        shiftKey = True
        c = shiftChars[c]
    if c in keyCodeMap:
        keyCode = keyCodeMap[c]
    else:
        keyCode = ord(c)
    return keyCode, shiftKey

shiftChars = {
    '~': '`',
    '!': '1',
    '@': '2',
    '#': '3',
    '$': '4',
    '%': '5',
    '^': '6',
    '&': '7',
    '*': '8',
    '(': '9',
    ')': '0',
    '_': '-',
    '+': '=',
    '{': '[',
    '}': ']',
    '|': '\\',
    ':': ';',
    '"': '\'',
    '<': ',',
    '>': '.',
    '?': '/'
}


keyCodeMap = {
    'a'                 : 0x00,
    's'                 : 0x01,
    'd'                 : 0x02,
    'f'                 : 0x03,
    'h'                 : 0x04,
    'g'                 : 0x05,
    'z'                 : 0x06,
    'x'                 : 0x07,
    'c'                 : 0x08,
    'v'                 : 0x09,
    'b'                 : 0x0B,
    'q'                 : 0x0C,
    'w'                 : 0x0D,
    'e'                 : 0x0E,
    'r'                 : 0x0F,
    'y'                 : 0x10,
    't'                 : 0x11,
    '1'                 : 0x12,
    '2'                 : 0x13,
    '3'                 : 0x14,
    '4'                 : 0x15,
    '6'                 : 0x16,
    '5'                 : 0x17,
    '='                 : 0x18,
    '9'                 : 0x19,
    '7'                 : 0x1A,
    '-'                 : 0x1B,
    '8'                 : 0x1C,
    '0'                 : 0x1D,
    ']'                 : 0x1E,
    'o'                 : 0x1F,
    'u'                 : 0x20,
    '['                 : 0x21,
    'i'                 : 0x22,
    'p'                 : 0x23,
    'l'                 : 0x25,
    'j'                 : 0x26,
    '\''                : 0x27,
    'k'                 : 0x28,
    ';'                 : 0x29,
    '\\'                : 0x2A,
    ','                 : 0x2B,
    '/'                 : 0x2C,
    'n'                 : 0x2D,
    'm'                 : 0x2E,
    '.'                 : 0x2F,
    '`'                 : 0x32,
    'k.'                : 0x41,
    'k*'                : 0x43,
    'k+'                : 0x45,
    'kclear'            : 0x47,
    'k/'                : 0x4B,
    'k\n'               : 0x4C,
    'k-'                : 0x4E,
    'k='                : 0x51,
    'k0'                : 0x52,
    'k1'                : 0x53,
    'k2'                : 0x54,
    'k3'                : 0x55,
    'k4'                : 0x56,
    'k5'                : 0x57,
    'k6'                : 0x58,
    'k7'                : 0x59,
    'k8'                : 0x5B,
    'k9'                : 0x5C,

    # keycodes for keys that are independent of keyboard layout
    '\n'                : 0x24,
    '\t'                : 0x30,
    ' '                 : 0x31,
    'del'               : 0x33,
    'delete'            : 0x33,
    'esc'               : 0x35,
    'escape'            : 0x35,
    'cmd'               : 0x37,
    'command'           : 0x37,
    'shift'             : 0x38,
    'caps lock'         : 0x39,
    'option'            : 0x3A,
    'ctrl'              : 0x3B,
    'control'           : 0x3B,
    'right shift'       : 0x3C,
    'rshift'            : 0x3C,
    'right option'      : 0x3D,
    'roption'           : 0x3D,
    'right control'     : 0x3E,
    'rcontrol'          : 0x3E,
    'fun'               : 0x3F,
    'function'          : 0x3F,
    'f17'               : 0x40,
    'volume up'         : 0x48,
    'volume down'       : 0x49,
    'mute'              : 0x4A,
    'f18'               : 0x4F,
    'f19'               : 0x50,
    'f20'               : 0x5A,
    'f5'                : 0x60,
    'f6'                : 0x61,
    'f7'                : 0x62,
    'f3'                : 0x63,
    'f8'                : 0x64,
    'f9'                : 0x65,
    'f11'               : 0x67,
    'f13'               : 0x69,
    'f16'               : 0x6A,
    'f14'               : 0x6B,
    'f10'               : 0x6D,
    'f12'               : 0x6F,
    'f15'               : 0x71,
    'help'              : 0x72,
    'home'              : 0x73,
    'pgup'              : 0x74,
    'page up'           : 0x74,
    'forward delete'    : 0x75,
    'f4'                : 0x76,
    'end'               : 0x77,
    'f2'                : 0x78,
    'page down'         : 0x79,
    'pgdn'              : 0x79,
    'f1'                : 0x7A,
    'left'              : 0x7B,
    'right'             : 0x7C,
    'down'              : 0x7D,
    'up'                : 0x7E
}
79
Phylliida

Le Documentation Python fournit cet extrait pour obtenir des caractères uniques à partir du clavier:

import termios, fcntl, sys, os
fd = sys.stdin.fileno()

oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)

oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

try:
    while 1:
        try:
            c = sys.stdin.read(1)
            print "Got character", repr(c)
        except IOError: pass
finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

Vous pouvez également utiliser le module PyHook pour faire votre travail.

19
enrico.bacis

L’un des moyens les plus simples que j’ai trouvé est d’utiliser le module pynput . peut être trouvé ici avec de jolis exemples également

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

ci-dessus est l'exemple élaboré pour moi et pour installer, allez
pour python 2:

    pip install pynput

pour python 3:

    pip3 install pynput
14
Prakash Palnati

Vous pouvez utiliser les méthodes de http://docs.python.org/2/library/msvcrt.html si vous utilisez Windows.

import msvcrt
....
while True:
    print "Doing a function"
    if msvcrt.kbhit():
        print "Key pressed: %s" % msvcrt.getch()
11
Tim

Ces fonctions, basées sur ce qui précède, semblent bien fonctionner pour extraire des caractères du clavier (bloquant et non bloquant):

import termios, fcntl, sys, os

def get_char_keyboard():
  fd = sys.stdin.fileno()

  oldterm = termios.tcgetattr(fd)
  newattr = termios.tcgetattr(fd)
  newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
  termios.tcsetattr(fd, termios.TCSANOW, newattr)

  c = None
  try:
    c = sys.stdin.read(1)
  except IOError: pass

  termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)

  return c

def get_char_keyboard_nonblock():
  fd = sys.stdin.fileno()

  oldterm = termios.tcgetattr(fd)
  newattr = termios.tcgetattr(fd)
  newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
  termios.tcsetattr(fd, termios.TCSANOW, newattr)

  oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
  fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

  c = None

  try:
    c = sys.stdin.read(1)
  except IOError: pass

  termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
  fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

  return c
5
mevans

Cela a fonctionné pour moi sur macOS Sierra et Python 2.7.10 et 3.6.3

import sys,tty,os,termios
def getkey():
    old_settings = termios.tcgetattr(sys.stdin)
    tty.setcbreak(sys.stdin.fileno())
    try:
        while True:
            b = os.read(sys.stdin.fileno(), 3).decode()
            if len(b) == 3:
                k = ord(b[2])
            else:
                k = ord(b)
            key_mapping = {
                127: 'backspace',
                10: 'return',
                32: 'space',
                9: 'tab',
                27: 'esc',
                65: 'up',
                66: 'down',
                67: 'right',
                68: 'left'
            }
            return key_mapping.get(k, chr(k))
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
try:
    while True:
        k = getkey()
        if k == 'esc':
            quit()
        else:
            print(k)
except (KeyboardInterrupt, SystemExit):
    os.system('stty sane')
    print('stopping.')
2
neoDev

J'ai écrit une implémentation plus facile à utiliser pour la réponse de @ enrico.bacis. Il prend en charge Linux (python2.7 et python3.5) et Windows (python2.7). Il supporte peut-être Mac OS, mais je ne l’ai pas testé. Si vous avez essayé sur Mac, dites-moi s'il vous plaît le résultat.

'''
Author: Yu Lou
Date: 2017-02-23

Based on the answer by @enrico.bacis in http://stackoverflow.com/a/13207724/4398908
and @Phylliida in http://stackoverflow.com/a/31736883/4398908
'''

# Import modules
try:
    try:
        import termios, fcntl, sys, os, curses # Import modules for Linux
    except ImportError:
        import msvcrt # Import module for Windows
except ImportError:
    raise Exception('This platform is not supported.')

class KeyGetterLinux:
    '''
    Implemented kbhit(), getch() and getchar() in Linux.

    Tested on Ubuntu 16.10(Linux 4.8.0), Python 2.7.12 and Python 3.5.2
    '''
    def __init__(self):
        self.buffer = '' # A buffer to store the character read by kbhit
        self.started = False # Whether initialization is complete

    def kbhit(self, echo = False):
        '''
        Return whether a key is hitten.
        '''
        if not self.buffer:
            if echo:
                self.buffer = self.getchar(block = False)
            else:
                self.buffer = self.getch(block = False)

        return bool(self.buffer)

    def getch(self, block = True):
        '''
        Return a single character without echo.
        If block is False and no input is currently available, return an empty string without waiting.
        '''
        try:
            curses.initscr()
            curses.noecho()
            return self.getchar(block)
        finally:
            curses.endwin()

    def getchar(self, block = True):
        '''
        Return a single character and echo.
        If block is False and no input is currently available, return an empty string without waiting.
        '''
        self._start()
        try:
            return self._getchar(block)
        finally:
            self._stop()

    def _getchar(self, block = True):
        '''
        Return a single character and echo.
        If block is False and no input is currently available, return a empty string without waiting.
        Should be called between self._start() and self._end()
        '''
        assert self.started, ('_getchar() is called before _start()')

        # Change the terminal setting
        if block:
            fcntl.fcntl(self.fd, fcntl.F_SETFL, self.old_flags & ~os.O_NONBLOCK)
        else:
            fcntl.fcntl(self.fd, fcntl.F_SETFL, self.old_flags | os.O_NONBLOCK)

        if self.buffer: # Use the character in buffer first
            result = self.buffer
            self.buffer = ''
        else:
            try:
                result = sys.stdin.read(1)
            except IOError: # In python 2.7, using read() when no input is available will result in IOError.
                return ''

        return result

    def _start(self):
        '''
        Initialize the terminal.
        '''
        assert not self.started, '_start() is called twice'

        self.fd = sys.stdin.fileno()

        self.old_attr = termios.tcgetattr(self.fd)

        new_attr = termios.tcgetattr(self.fd)
        new_attr[3] = new_attr[3] & ~termios.ICANON
        termios.tcsetattr(self.fd, termios.TCSANOW, new_attr)

        self.old_flags = fcntl.fcntl(self.fd, fcntl.F_GETFL)

        self.started = True

    def _stop(self):
        '''
        Restore the terminal.
        '''
        assert self.started, '_start() is not called'

        termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_attr)
        fcntl.fcntl(self.fd, fcntl.F_SETFL, self.old_flags)

        self.started = False

    # Magic functions for context manager
    def __enter__(self):
        self._start()
        self.getchar = self._getchar # No need for self._start() now
        return self

    def __exit__(self, type, value, traceback):
        self._stop()
        return False

class KeyGetterWindows:
    '''
    kbhit() and getchar() in Windows.

    Tested on Windows 7 64 bit, Python 2.7.1
    '''
    def kbhit(self, echo):
        return msvcrt.kbhit()

    def getchar(self, block = True):
        if not block and not msvcrt.kbhit():
            return ''
        return msvcrt.getchar()

    def getch(self, block = True):
        if not block and not msvcrt.kbhit():
            return ''
        return msvcrt.getch()

    _getchar = getchar

    # Magic functions for context manager
    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        return False

try:
    import termios
    KeyGetter = KeyGetterLinux # Use KeyGetterLinux if termios exists
except ImportError:
    KeyGetter = KeyGetterWindows # Use KeyGetterWindows otherwise

Ceci est un exemple (supposons que vous ayez enregistré les codes ci-dessus dans 'key_getter.py'):

from key_getter import KeyGetter
import time

def test1(): # Test with block=False
    print('test1')

    k = KeyGetter()
    try:
        while True:
            if k.kbhit():
                print('Got', repr(k.getch(False)))
                print('Got', repr(k.getch(False)))
            else:
                print('Nothing')

            time.sleep(0.5)
    except KeyboardInterrupt:
        pass
    print(input('Enter something:'))

def test2(): # Test context manager with block=True
    print('test2')

    with KeyGetter() as k:
        try:
            while True:
                if k.kbhit():
                    print('Got', repr(k.getchar(True)))
                    print('Got', repr(k.getchar(True)))
                else:
                    print('Nothing')

                time.sleep(0.5)
        except KeyboardInterrupt:
            pass
    print(input('Enter something:'))

test1()
test2()
1
LouYu

Inspiré du code trouvé ci-dessus (crédits), la version macOS que je cherchais:

import termios
import sys
import fcntl
import os

def getKeyCode(blocking = True):
    fd = sys.stdin.fileno()
    oldterm = termios.tcgetattr(fd)
    newattr = termios.tcgetattr(fd)
    newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSANOW, newattr)
    if not blocking:
        oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
    try:
        return ord(sys.stdin.read(1))
    except IOError:
        return 0
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
        if not blocking:
            fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

def getKeyStroke():
    code  = getKeyCode()
    if code == 27:
        code2 = getKeyCode(blocking = False)
        if code2 == 0:
            return "esc"
        Elif code2 == 91:
            code3 = getKeyCode(blocking = False)
            if code3 == 65:
                return "up"
            Elif code3 == 66:
                return "down"
            Elif code3 == 68:
                return "left"
            Elif code3 == 67:
                return "right"
            else:
                return "esc?"
    Elif code == 127:
        return "backspace"
    Elif code == 9:
        return "tab"
    Elif code == 10:
        return "return"
    Elif code == 195 or code == 194:        
        code2 = getKeyCode(blocking = False)
        return chr(code)+chr(code2) # utf-8 char
    else:
        return chr(code)


while True:
    print getKeyStroke()

2017-11-09, EDITED: Non testé avec Python 3

1
bm842

Depuis la date de cette question, une bibliothèque Python a abordé ce sujet. La bibliothèque pynput, de Moses Palmer, est idéale pour capturer les événements de clavier et de souris de manière très simple.

(Attention, il manque le 'i' dans pynput - ça m'a aussi manqué ... ;-))

0
Schmouk

Cela doit être exécuté en tant que root: (Attention, il s'agit d'un enregistreur de frappe système)

#!/usr/bin/python3

import signal
import keyboard
import time
import os


if not os.geteuid() == 0:
  print("This script needs to be run as root.")
  exit()

def exitNice(signum, frame):
  global running 
  running = False

def keyEvent(e):
  global running
  if e.event_type == "up":
    print("Key up: " + str(e.name))
  if e.event_type == "down":
    print("Key down: " + str(e.name))
  if e.name == "q":
    exitNice("", "")
    print("Quitting")

running = True
signal.signal(signal.SIGINT, exitNice)
keyboard.hook(keyEvent)

print("Press 'q' to quit")
fps = 1/24
while running:
  time.sleep(fps)

0
David Hamner