単純な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)
私はこの問題について多くのことを考えてきましたが、いくつかの異なる動作が必要になる可能性があります。私はそれらのほとんどをUnixとWindows向けに実装してきましたが、完了したらここに投稿します。
input
またはraw_input
、ユーザーが改行を押すと入力したテキストを返すブロック関数。ユーザーは、キーが押されたときに、そのキーを待たずに何かをできるようにしたいだけです(そのため、これは非ブロッキングでなければなりません)。したがって、それらはpoll()関数を呼び出し、キーを返すか、Noneを返します。これは、損失がある(ポーリング間で時間がかかりすぎる場合はキーを見逃す可能性がある)か、損失がない(ポーラーは押されたすべてのキーの履歴を保存するため、poll()関数がそれらを要求すると常に返されます)押された順序で)。
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/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()
プログラムの実行中にキーが押された状態で呼び出されるコールバック(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
}
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 モジュールを使用してジョブを完了させることもできます。
私が見つけた最も簡単な方法の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
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()
上記に基づいたこれらの関数は、キーボードから文字を取得するためにうまく機能するようです(ブロックおよび非ブロック):
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
これは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.')
@ 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()
上記のコード(クレジット)から着想を得た、私が探していた単純なブロッキング(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ではテストされていません
さて、この質問投稿の日付以来、Pythonライブラリはこのトピックに対処しました。MosesPalmerのpynputライブラリは、非常に簡単な方法でキーボードおよびマウスイベントをキャッチできます。
(pynputで行方不明の「i」に気をつけてください-私もそれを見逃しました... ;-))
これはルートとして実行する必要があります:(警告、これはシステム全体のキーロガーです)
#!/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)