raw_input - python input()
¿Cuál es la forma más sencilla de detectar la entrada del teclado en python desde la terminal? (9)
Editar:
He pensado mucho sobre este problema, y hay algunos comportamientos diferentes que uno podría desear. He estado implementando la mayoría de ellos para Unix y Windows, y los publicaré aquí una vez que terminen.
Captura de clave síncrona / de bloqueo:
- Una
input
simple oraw_input
, una función de bloqueo que devuelve texto escrito por un usuario una vez que presiona una nueva línea. - Una función de bloqueo simple que espera que el usuario presione una sola tecla y luego devuelve esa clave
Captura de clave asincrónica:
- Una devolución de llamada que se invoca con la tecla presionada cada vez que el usuario ingresa una clave en el símbolo del sistema, incluso cuando escribe cosas en un intérprete (un registrador de pulsaciones de teclas)
- Una devolución de llamada que se llama con el texto escrito después de que el usuario presione enter (un registrador de pulsaciones menos en tiempo real)
- Una devolución de llamada que se ejecuta con las teclas presionadas cuando un programa se está ejecutando (por ejemplo, en un bucle for o while)
Votación:
El usuario simplemente quiere poder hacer algo cuando se presiona una tecla, sin tener que esperar esa tecla (por lo que debería ser no bloqueante). Por lo tanto, llaman a una función poll () y devuelve una clave o devuelve None. Esto puede ser con pérdida (si tardan demasiado entre una encuesta y otra) o sin pérdida (el sondeador almacenará el historial de todas las teclas presionadas, de modo que cuando la función poll () las solicite siempre serán devueltas en el orden presionado).
Lo mismo que 1, excepto que la encuesta solo devuelve algo una vez que el usuario presiona una nueva línea.
Robots:
Estos son algo que se puede llamar para disparar eventos de teclado mediante programación. Esto se puede usar junto con capturas de teclas para devolverlas al usuario
Implementaciones
Captura de clave síncrona / de bloqueo:
Una input
simple o raw_input
, una función de bloqueo que devuelve texto escrito por un usuario una vez que presiona una nueva línea.
typedString = raw_input()
Una función de bloqueo simple que espera que el usuario presione una sola tecla y luego devuelve esa clave
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
Captura de clave asincrónica:
Una devolución de llamada que se invoca con la tecla presionada cada vez que el usuario ingresa una clave en el símbolo del sistema, incluso cuando escribe cosas en un intérprete (un registrador de pulsaciones de teclas)
Una devolución de llamada que se llama con el texto escrito después de que el usuario presione enter (un registrador de pulsaciones menos en tiempo real)
Windows:
Utiliza el Robot de Windows que se muestra a continuación, nombrando el 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()
Una devolución de llamada que se ejecuta con las teclas presionadas cuando un programa se está ejecutando (por ejemplo, en un bucle for o 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()
Votación:
El usuario simplemente quiere poder hacer algo cuando se presiona una tecla, sin tener que esperar esa tecla (por lo que debería ser no bloqueante). Por lo tanto, llaman a una función poll () y devuelve una clave o devuelve None. Esto puede ser con pérdida (si tardan demasiado entre una encuesta y otra) o sin pérdida (el sondeador almacenará el historial de todas las teclas presionadas, de modo que cuando la función poll () las solicite siempre serán devueltas en el orden presionado).
Windows y OS X (y tal vez 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
Caso de uso simple:
with KeyPoller() as keyPoller:
while True:
c = keyPoller.poll()
if not c is None:
if c == "c":
break
print c
Lo mismo que arriba, excepto que la encuesta solo devuelve algo una vez que el usuario presiona una nueva línea.
Robots:
Estos son algo que se puede llamar para disparar eventos de teclado mediante programación. Esto se puede usar junto con capturas de teclas para devolverlas al usuario
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
}
Tengo un script de python simple, que tiene algunas funciones que se ejecutan en un bucle (estoy tomando lecturas de sensor).
while True:
print "Doing a function"
Si se presiona el teclado, me gustaría imprimir "tecla presionada".
¿Cuál es la forma más simple de hacer esto en Python? He buscado alto y bajo. He descubierto cómo hacerlo con pygame, pero preferiría hacerlo sin él. Si tengo que usar pygame ¿es posible no tener una ventana separada para la aplicación ?:
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)
Escribí una implementación más fácil de usar para la respuesta de @ enrico.bacis. Admite tanto Linux (python2.7 y python3.5) como Windows (python2.7). Puede ser compatible con Mac OS, pero no lo probé. Si lo probaste en Mac, por favor dime el resultado.
''''''
Author: Yu Lou
Date: 2017-02-23
Based on the answer by @enrico.bacis in http://.com/a/13207724/4398908
and @Phylliida in http://.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
Este es un ejemplo (supongamos que ha guardado los códigos de arriba en ''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()
Estas funciones, basadas en lo anterior, parecen funcionar bien para obtener caracteres del teclado (bloqueo y no bloqueo):
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
Esto funcionó para mí en macOS Sierra y Python 2.7.10 y 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.'')
Inspirado en el código que se encuentra arriba (créditos), la versión de macOS de bloqueo simple (también conocida como no consumidora de CPU) que estaba buscando:
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, EDITADO : No probado con Python 3
La Documentación de Python proporciona este fragmento para obtener caracteres individuales del teclado:
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)
También puede usar el módulo PyHook para hacer su trabajo.
Puede usar los métodos de http://docs.python.org/2/library/msvcrt.html si está en Windows.
import msvcrt
....
while True:
print "Doing a function"
if msvcrt.kbhit():
print "Key pressed: %s" % msvcrt.getch()
Una de las maneras más simples que encontré es usar el módulo pynput. se puede encontrar aquí con buenos ejemplos también
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()
arriba está el ejemplo resuelto para mí y para instalarlo, vaya
sudo pip install pynput (pip3 if python3.*)
import turtle
wn = turtle.Screen()
turtle = turtle.Turtle()
def printLetter():
print("a")
turtle.listen()
turtle.onkey(printLetter, "a")