web-dev-qa-db-ja.com

端末からpythonでキーボード入力を検出する最も簡単な方法は何ですか?

単純なpythonスクリプト、ループで実行されるいくつかの関数があります(センサーの読み取り値を取得しています)。

while True:
    print "Doing a function"

キーボードが押された場合、「キーが押されました」と印刷したいです。

Pythonでこれを行う最も簡単な方法は何ですか?高低を検索しました。私はpygameでそれをする方法を見つけましたが、私はむしろそれなしでやりたいです。 pygameを使用する必要がある場合、アプリケーション用に別のウィンドウを持たないことは可能ですか?:

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

編集:

私はこの問題について多くのことを考えてきましたが、いくつかの異なる動作が必要になる可能性があります。私はそれらのほとんどをUnixとWindows向けに実装してきましたが、完了したらここに投稿します。

同期/ブロッキングキーキャプチャ:

  1. 単純なinputまたはraw_input、ユーザーが改行を押すと入力したテキストを返すブロック関数。
  2. ユーザーが単一のキーを押すのを待ってからそのキーを返す単純なブロック関数

非同期キーキャプチャ:

  1. インタプリタ(キーロガー)に入力する場合でも、ユーザーがコマンドプロンプトにキーを入力するたびに、押されたキーで呼び出されるコールバック
  2. ユーザーがEnterキーを押した後に入力されたテキストで呼び出されるコールバック(リアルタイム性の低いキーロガー)
  3. プログラムの実行中にキーが押された状態で呼び出されるコールバック(forループやwhileループなど)

ポーリング:

  1. ユーザーは、キーが押されたときに、そのキーを待たずに何かをできるようにしたいだけです(そのため、これは非ブロッキングでなければなりません)。したがって、それらはpoll()関数を呼び出し、キーを返すか、Noneを返します。これは、損失がある(ポーリング間で時間がかかりすぎる場合はキーを見逃す可能性がある)か、損失がない(ポーラーは押されたすべてのキーの履歴を保存するため、poll()関数がそれらを要求すると常に返されます)押された順序で)。

  2. 1と同じですが、ユーザーが改行を押すとポーリングが何かを返すだけです。

ロボット:

これらは、プログラムでキーボードイベントを起動するために呼び出すことができるものです。これをキーキャプチャと一緒に使用して、ユーザーにエコーバックすることができます。

実装

同期/ブロッキングキーキャプチャ:

単純なinputまたはraw_input、ユーザーが改行を押すと入力したテキストを返すブロック関数。

typedString = raw_input()

ユーザーが単一のキーを押すのを待ってからそのキーを返す単純なブロック関数

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

非同期キーキャプチャ:

インタプリタ(キーロガー)に入力する場合でも、ユーザーがコマンドプロンプトにキーを入力するたびに、押されたキーで呼び出されるコールバック

ユーザーがEnterキーを押した後に入力されたテキストで呼び出されるコールバック(リアルタイム性の低いキーロガー)

Windows:

これは、以下のWindows Robotを使用して、スクリプトにkeyPress.pyという名前を付けます。

# Some if this is from http://nullege.com/codes/show/[email protected]@[email protected]@[email protected]@subprocess.py/380/win32api.GetStdHandle
# and
# http://nullege.com/codes/show/[email protected]@[email protected]@[email protected]@winpexpect.py/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()

プログラムの実行中にキーが押された状態で呼び出されるコールバック(forループやwhileループなど)

Windows:

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

ポーリング:

ユーザーは、キーが押されたときに、そのキーを待たずに何かをできるようにしたいだけです(そのため、これは非ブロッキングでなければなりません)。したがって、それらはpoll()関数を呼び出し、キーを返すか、Noneを返します。これは、損失がある(ポーリング間で時間がかかりすぎる場合はキーを見逃す可能性がある)か、損失がない(ポーラーは押されたすべてのキーの履歴を保存するため、poll()関数がそれらを要求すると常に返されます)押された順序で)。

WindowsおよびOS X(およびおそらく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

シンプルなユースケース:

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

上記と同じですが、ユーザーが改行を押すとポーリングが何かを返すだけです。

ロボット:

これらは、プログラムでキーボードイベントを起動するために呼び出すことができるものです。これをキーキャプチャと一緒に使用して、ユーザーにエコーバックすることができます。

Windows:

# 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

Pythonドキュメント は、このスニペットを提供して、キーボードから単一の文字を取得します。

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)

PyHook モジュールを使用してジョブを完了させることもできます。

19
enrico.bacis

私が見つけた最も簡単な方法の1つは、pynputモジュールを使用することです。 ここにニースの例もあります

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

上記は私のためにうまくいき、インストールするための例です
for python 2:

    pip install pynput

for python 3:

    pip3 install pynput
14
Prakash Palnati

Windowsを使用している場合は、 http://docs.python.org/2/library/msvcrt.html のメソッドを使用できます。

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

上記に基づいたこれらの関数は、キーボードから文字を取得するためにうまく機能するようです(ブロックおよび非ブロック):

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

これはmacOS SierraとPython 2.7.10および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

@ enrico.bacisの回答用に、より使いやすい実装を作成しました。 Linux(python2.7およびpython3.5)とWindows(python2.7)の両方をサポートしています。 Mac OSをサポートするかもしれませんが、私はテストしませんでした。 Macで試した場合は、結果を教えてください。

'''
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

これは例です(上記のコードを '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

上記のコード(クレジット)から着想を得た、私が探していた単純なブロッキング(CPUを消費しない)macOSバージョン:

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、編集済み:Python 3ではテストされていません

1
bm842

さて、この質問投稿の日付以来、Pythonライブラリはこのトピックに対処しました。MosesPalmerのpynputライブラリは、非常に簡単な方法でキーボードおよびマウスイベントをキャッチできます。

(pynputで行方不明の「i」に気をつけてください-私もそれを見逃しました... ;-))

0
Schmouk

これはルートとして実行する必要があります:(警告、これはシステム全体のキーロガーです)

#!/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