1、设置终端为 raw mode:
import sys from termios import * _old_settings = tcgetattr(sys.stdin.fileno()) IFLAG, OFLAG, CFLAG, LFLAG, ISPEED, OSPEED, CC = 0, 1, 2, 3, 4, 5, 6 def setraw_input(fd, when=TCSAFLUSH): """Put the input of the terminal into a raw mode.""" mode = tcgetattr(fd) mode[IFLAG] = mode[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON) # mode[OFLAG] = mode[OFLAG] & ~(OPOST) mode[CFLAG] = mode[CFLAG] & ~(CSIZE | PARENB) mode[CFLAG] = mode[CFLAG] | CS8 mode[LFLAG] = mode[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG) mode[CC][VMIN] = 1 mode[CC][VTIME] = 0 tcsetattr(fd, when, mode) return
setraw_input 函数参考 python 中的 tty.setraw 函数实现,仅仅取消 OFLAG 的改变,使得终端能够继续处理输出中的特殊字符,以避免输出乱码。
2、键盘输入采样:
import select def _get_key(): key = '' for index in range(0, 10): rlist, _, _ = select.select([sys.stdin], [], [], 0) if rlist: key = sys.stdin.read(1) return key import threading, traceback _timer = None from functools import wraps def input_wrap(func): @wraps(func) def wrapper(*args, **kwargs): try: key = _get_key() func(key) if key == '\x03': print("ctrl + c") raise Exception("ctrl + c") global _timer _timer = threading.Timer(0.1, wrapper) _timer.start() except Exception: input_wrap_reset() return return wrapper
键盘输入采样频率为 10Hz (0.1s), _get_key 函数中的 for 循环次数计算如下:
假设键盘输入速率 25char/s (长按不放) 10 (循环次数) = 25 (输入速率) x 2 (采样定理) x 2 (安全系数) / 10 (采样频率)
3、自定义键盘输入处理函数:
@input_wrap def print_key(key): if key != "": print(key) return
4、完整代码:
input_wrap.py
#!/usr/bin/env python import sys from termios import * _old_settings = tcgetattr(sys.stdin.fileno()) IFLAG, OFLAG, CFLAG, LFLAG, ISPEED, OSPEED, CC = 0, 1, 2, 3, 4, 5, 6 def setraw_input(fd, when=TCSAFLUSH): """Put the input of the terminal into a raw mode.""" mode = tcgetattr(fd) mode[IFLAG] = mode[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON) # mode[OFLAG] = mode[OFLAG] & ~(OPOST) mode[CFLAG] = mode[CFLAG] & ~(CSIZE | PARENB) mode[CFLAG] = mode[CFLAG] | CS8 mode[LFLAG] = mode[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG) mode[CC][VMIN] = 1 mode[CC][VTIME] = 0 tcsetattr(fd, when, mode) return def input_wrap_set(): setraw_input(sys.stdin.fileno()) return def input_wrap_reset(): tcsetattr(sys.stdin.fileno(), TCSAFLUSH, _old_settings) return import select def _get_key(): key = '' for index in range(0, 10): rlist, _, _ = select.select([sys.stdin], [], [], 0) if rlist: key = sys.stdin.read(1) return key import threading, traceback _timer = None from functools import wraps def input_wrap(func): @wraps(func) def wrapper(*args, **kwargs): try: key = _get_key() func(key) if key == '\x03': print("ctrl + c") raise Exception("ctrl + c") global _timer _timer = threading.Timer(0.1, wrapper) _timer.start() except Exception: input_wrap_reset() return return wrapper @input_wrap def print_key(key): if key != "": print(key) return if __name__ == "__main__": input_wrap_set() print("hello, world!\thello, world!") print_key() import time try: while True: time.sleep(1) except: pass finally: input_wrap_reset() print("hello, world!\thello, world!")