在python中轮询键盘(检测按键)
- 2025-02-13 08:36:00
- admin 原创
- 41
问题描述:
如何从控制台 Python 应用程序轮询键盘?具体来说,我想在许多其他 I/O 活动(套接字选择、串行端口访问等)中执行类似操作:
while True:
# doing amazing pythonic embedded stuff
# ...
# periodically do a non-blocking check to see if
# we are being told to do something else
x = keyboard.read(1000, timeout = 0)
if len(x):
# ok, some key got pressed
# do something
在 Windows 上执行此操作的正确 Python 方式是什么?此外,尽管不需要,但移植到 Linux 也不错。
解决方案 1:
标准方法是使用选择模块。
但是,这在 Windows 上不起作用。为此,您可以使用msvcrt模块的键盘轮询。
通常,这是通过多个线程来完成的——每个被“监视”的设备一个线程加上可能需要被设备中断的后台进程。
解决方案 2:
使用 curses 模块的解决方案。打印与按下的每个键相对应的数值:
import curses
def main(stdscr):
# do not wait for input when calling getch
stdscr.nodelay(1)
while True:
# get keyboard input, returns -1 if none available
c = stdscr.getch()
if c != -1:
# print numeric value
stdscr.addstr(str(c) + ' ')
stdscr.refresh()
# return curser to start position
stdscr.move(0, 0)
if __name__ == '__main__':
curses.wrapper(main)
解决方案 3:
好吧,由于我尝试在评论中发布解决方案失败了,所以这就是我想说的。我可以使用以下代码从本机 Python(在 Windows 上,但不是在其他任何地方)完成我想要的操作:
import msvcrt
def kbfunc():
x = msvcrt.kbhit()
if x:
ret = ord(msvcrt.getch())
else:
ret = 0
return ret
解决方案 4:
这些答案对我来说都不太适用。这个包 pynput 正好满足了我的需要。
https://pypi.python.org/pypi/pynput
from pynput.keyboard import Key, Listener
def on_press(key):
print('{0} pressed'.format(
key))
def on_release(key):
print('{0} release'.format(
key))
if key == Key.esc:
# Stop listener
return False
# Collect events until released
with Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
解决方案 5:
import sys
import select
def heardEnter():
i,o,e = select.select([sys.stdin],[],[],0.0001)
for s in i:
if s == sys.stdin:
input = sys.stdin.readline()
return True
return False
解决方案 6:
来自评论:
import msvcrt # built-in module
def kbfunc():
return ord(msvcrt.getch()) if msvcrt.kbhit() else 0
谢谢你的帮助。我最终编写了一个名为 PyKeyboardAccess.dll 的 C DLL 并访问 crt conio 函数,导出此例程:
#include <conio.h>
int kb_inkey () {
int rc;
int key;
key = _kbhit();
if (key == 0) {
rc = 0;
} else {
rc = _getch();
}
return rc;
}
我使用 ctypes 模块(内置于 python 2.5)在 python 中访问它:
import ctypes
import time
# first, load the DLL
try:
kblib = ctypes.CDLL("PyKeyboardAccess.dll")
except:
raise ("Error Loading PyKeyboardAccess.dll")
# now, find our function
try:
kbfunc = kblib.kb_inkey
except:
raise ("Could not find the kb_inkey function in the dll!")
# Ok, now let's demo the capability
while True:
x = kbfunc()
if x != 0:
print "Got key: %d" % x
else:
time.sleep(.01)
解决方案 7:
kbhit
我在http://home.wlu.edu/~levys/software/kbhit.py上发现了一个跨平台实现(进行了编辑以删除不相关的代码):
import os
if os.name == 'nt':
import msvcrt
else:
import sys, select
def kbhit():
''' Returns True if a keypress is waiting to be read in stdin, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select.select([sys.stdin], [], [], 0)
return dr != []
确保read()
等待字符——该函数将一直返回,True
直到您这样做!
解决方案 8:
您可以看看pygame如何处理这个问题以窃取一些想法。
解决方案 9:
我用它来检查按键,非常简单:
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import curses, time
def main(stdscr):
"""checking for keypress"""
stdscr.nodelay(True) # do not wait for input when calling getch
return stdscr.getch()
while True:
print("key:", curses.wrapper(main)) # prints: 'key: 97' for 'a' pressed
# '-1' on no presses
time.sleep(1)
虽然 curses 在 Windows 上无法运行,但有一个 'unicurses' 版本,据说可以在 Linux、Windows、Mac 上运行,但我无法让它运行
解决方案 10:
另一个选择是使用sshkeyboard库来对按键做出反应,而不是定期轮询它们,并且可能会错过按键:
from sshkeyboard import listen_keyboard, stop_listening
def press(key):
print(f"'{key}' pressed")
if key == "z":
stop_listening()
listen_keyboard(on_press=press)
只需pip install sshkeyboard
使用它。
解决方案 11:
这可以使用 python 中的“pynput”模块来完成,您按下一个键就会打印出来,就这么简单!
PIP 在命令提示符中安装模块,输入以下文本并按回车键
pip install pynput
运行以下代码:
from pynput.keyboard import Key, Listener
def pressed(key):
print('Pressed:',key)
def released(key):
print('Released:',key)
if key == Key.enter:
# Stop detecting when enter key is pressed
return False
# Below loop for Detcting keys runs until enter key is pressed
with Listener(on_press=pressed, on_release=released) as detector:
detector.join()
Key.enter
您可以通过将代码第 8 行中的任意键更改为其他键来结束循环。
解决方案 12:
如果结合使用 time.sleep、threading.Thread 和 sys.stdin.read,您可以轻松等待指定的时间输入然后继续,而且这也应该是跨平台兼容的。
t = threading.Thread(target=sys.stdin.read(1) args=(1,))
t.start()
time.sleep(5)
t.join()
你也可以将其放入如下函数中
def timed_getch(self, bytes=1, timeout=1):
t = threading.Thread(target=sys.stdin.read, args=(bytes,))
t.start()
time.sleep(timeout)
t.join()
del t
虽然这不会返回任何东西,所以你应该使用多处理池模块,你可以在这里找到它:如何从 python 中的线程获取返回值?