¿Cuál es la forma más sencilla de detectar entradas de teclado en python desde el terminal?

Tengo una secuencia de comandos de Python simple, que tiene algunas funciones que se ejecutan en un bucle (estoy tomando lecturas de sensores).

while True: print "Doing a function" 

Si se presiona el teclado, me gustaría imprimir “tecla presionada”.

¿Cuál es la forma más sencilla de hacer esto en Python? He buscado alto y bajo. He descubierto cómo hacerlo con pygame, pero prefiero hacerlo sin. 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) 

Editar:

He pensado mucho en 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 estén listos.

Captura de llave sincrónica / bloqueadora:

  1. Una input simple o raw_input , una función de locking que devuelve el texto escrito por un usuario una vez que presiona una nueva línea.
  2. Una función de locking simple que espera a que el usuario presione una sola tecla y luego la devuelve

Captura asíncrona de claves:

  1. Una callback que se llama con la tecla presionada cada vez que el usuario escribe una tecla en el símbolo del sistema, incluso cuando escribe cosas en un intérprete (un keylogger)
  2. Una callback que se llama con el texto escrito después de que el usuario presiona enter (un keylogger menos en tiempo real)
  3. Una callback que se llama con las teclas presionadas cuando se está ejecutando un progtwig (por ejemplo, en un bucle for o en un bucle while)

Votación:

  1. El usuario simplemente quiere poder hacer algo cuando se presiona una tecla, sin tener que esperar esa tecla (por lo tanto, esto no debe ser bloqueado). Por lo tanto, llaman a una función de sondeo () y eso devuelve una clave o devuelve Ninguno. Esto puede ser con pérdida (si se demoran entre cada encuesta) puede faltar una tecla) o sin pérdida (el encuestador almacenará el historial de todas las teclas presionadas, de modo que cuando la función de encuesta () las solicite, siempre se devolverán en el orden presionado).

  2. 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 activar eventos de teclado mediante progtwigción. Esto se puede utilizar junto con las capturas de teclas para devolverlas al usuario

Implementaciones

Captura de llave sincrónica / bloqueadora:

Una input simple o raw_input , una función de locking que devuelve el texto escrito por un usuario una vez que presiona una nueva línea.

 typedString = raw_input() 

Una función de locking simple que espera a que el usuario presione una sola tecla y luego la devuelve

 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 asíncrona de claves:

Una callback que se llama con la tecla presionada cada vez que el usuario escribe una tecla en el símbolo del sistema, incluso cuando escribe cosas en un intérprete (un keylogger)

Una callback que se llama con el texto escrito después de que el usuario presiona enter (un keylogger menos en tiempo real)

Windows:

Esto utiliza el Robot de Windows que se indica a continuación, nombrando el script keyPress.py

 # Some if this is from http://nullege.com/codes/show/src@e@i@einstein-HEAD@Python25Einstein@Lib@subprocess.py/380/win32api.GetStdHandle # and # http://nullege.com/codes/show/src@v@i@VistA-HEAD@Python@Pexpect@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() 

Una callback que se llama con las teclas presionadas cuando se está ejecutando un progtwig (por ejemplo, en un bucle for o en un bucle 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 tanto, esto no debe ser bloqueado). Por lo tanto, llaman a una función de sondeo () y eso devuelve una clave o devuelve Ninguno. Esto puede ser con pérdida (si se demoran entre cada encuesta) puede faltar una tecla) o sin pérdida (el encuestador almacenará el historial de todas las teclas presionadas, de modo que cuando la función de encuesta () las solicite, siempre se devolverán en el orden presionado).

Windows y OS X (y quizás 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 activar eventos de teclado mediante progtwigción. Esto se puede utilizar junto con las 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 } 

La documentación de Python proporciona este fragmento de código 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 formas más sencillas 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() 

El anterior es el ejemplo resuelto para mí y para instalar, vaya

 sudo pip install pynput (pip3 if python3.*) 

Estas funciones, basadas en lo anterior, parecen funcionar bien para obtener caracteres del teclado (locking y no locking):

 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 me funcionó 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.') 

Escribí una implementación más fácil de usar para la respuesta de @ enrico.bacis. Es compatible con Linux (python2.7 y python3.5) y Windows (python2.7). Puede que sea compatible con Mac OS, pero no lo probé. Si lo intentaste en Mac, por favor dime el resultado.

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

Este es un ejemplo (suponga que guardó los códigos anteriores 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() 

Inspirado en el código que se encuentra arriba (créditos), la versión macOS de locking simple (también conocida como que no consume 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

Esto debe ejecutarse como root: (Advertencia, este es un keylogger de todo el sistema)

 #!/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) 
 import turtle wn = turtle.Screen() turtle = turtle.Turtle() def printLetter(): print("a") turtle.listen() turtle.onkey(printLetter, "a")