在python中轮询键盘(检测按键)
Posted
技术标签:
【中文标题】在python中轮询键盘(检测按键)【英文标题】:Polling the keyboard (detect a keypress) in python 【发布时间】:2010-09-22 11:14:52 【问题描述】:如何从控制台 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 的可移植性也不错,尽管这不是必需的。
【问题讨论】:
只是为了让其他人知道,我发现大多数涉及选择或线程库的解决方案在 IDLE 中都无法正常工作。但是,它们 all 在 CLI 上运行良好,即python /home/pi/poll_keyboard.py
总的来说,我认为对按键做出反应而不是定期轮询它们是更强大的解决方案,因为您不会错过按键。请参阅下面的答案。
【参考方案1】:
标准方法是使用select 模块。
但是,这在 Windows 上不起作用。为此,您可以使用msvcrt 模块的键盘轮询。
通常,这是通过多个线程来完成的——每个设备一个被“监视”加上可能需要被设备中断的后台进程。
【讨论】:
如果我错了,请纠正我,但根据我的经验,msvcrt 仅在您在命令窗口中运行程序时才有效,即。不在 IDLE 中,也没有 GUI... @digitalHamster0:任何用自定义对象(例如 IDLE、大多数 GUI)替换sys.stdin
的东西都会产生这种效果。当sys.stdin
不是真正的文件时,你不能使用select
;当它没有连接到“真实”控制台时,您不能使用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)
【讨论】:
OZ123:可以。见***.com/questions/32417379/… 在无头主机上通过 SSH 术语使用诅咒时遇到问题。问题严重扰乱了终端 - 要求它在每次运行之间为reset
。它确实有效,即检测按键。必须有更智能的解决方案。【参考方案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()
【讨论】:
这对我有用,除了按下的键在按下后立即回显到屏幕上,并且无法禁用它。 github.com/moses-palmer/pynput/issues/47 另外,当程序退出时,字符会被缓冲并额外出现在命令行上。这似乎是 Linux 实现的限制,但在 Windows 上可以正常工作。 当脚本通过 ssh 运行时,此解决方案不起作用。它出现错误:'Xlib.error.DisplayNameError: Bad display name "".' 正如 David 上面提到的 - 这对于无头实例来说不是一个好的解决方案,因为它依赖于 Xserver。import Xlib.display
【参考方案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
【讨论】:
没有工作。出现错误:select.error: (10093, '要么应用程序没有调用 WSAStartup,要么 WSAStartup 失败') 我多次听说 MS Windows 上的 select 系统调用不支持常规文件描述符,仅适用于套接字。 (我不知道 select() 的 Python 实现是否曾经在幕后解决过这个问题)。 对我来说,这只会在我按下 Enter 后检测到按键。 @MarkSmith:那是因为程序在按下 enter 或 control-D (*) 之前没有收到输入,它仍然在内核的“行编辑缓冲区”中。 (如果在缓冲区中没有字符的情况下按 control-D,它将关闭终端。) // 为了在类 unix 系统上工作,终端必须设置为“raw”或“cbreak”模式而不是“cooked” '。我认为这是通过 stdin 上的一些 ioctl 完成的。 @JonathanHartley:(见我之前的评论。)【参考方案6】:来自cmets:
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)
【讨论】:
这比内置的 msvcrt.kbhit() 有什么好处?它有什么优势? 你完全正确!我看错了你的帖子;我没有意识到有一个名为 msvcrt 的 python 模块!我只是以为您的意思是“使用 ms crt”,然后我开始考虑线程并且没有连接点。你是绝对正确的。 我做了同样的事情: import msvcrt def kbfunc(): x = msvcrt.kbhit() if x: ret = ord(msvcrt.getch()) else: ret = 0 return ret 请不要使用这样的 lambda。 "x = lambda" 应该拼写为 "def x():" 保存 lambda 会混淆 n00bz 并让有经验的人疯狂地试图解释它。 哈哈!那不是 lambda。这就是“cmets”字段如何重新格式化我将代码放入评论的尝试。顺便说一句,保存 lambda 也让我感到困惑,而且我不是 python n00b :-)【参考方案7】:我在http://home.wlu.edu/~levys/software/kbhit.py 遇到了kbhit
的跨平台实现(进行了编辑以删除不相关的代码):
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
直到你这样做!
【讨论】:
这仍然是最新的吗?当我调用选择版本时,我总是会在 dr 中恢复内容。如果它仍然有效,你能把它放在上下文中吗?我有一个“while true”循环,如果按下某个键,我想摆脱它。 @Mastiff 也许您在按照建议检测到等待字符后不会read()
。【参考方案8】:
你可以看看pygame 是如何处理这个来窃取一些想法的。
【讨论】:
PyGame 事件处理仅适用于 GUI,而不是 OP 要求的控制台。【参考方案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 上运行,但我无法让它运行
【讨论】:
PyPI 上还有windows-curses
。【参考方案10】:
这可以使用 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()
您可以通过将代码第 8 行中的 Key.enter
更改为其他键来使用您想要的任何键结束循环。
【讨论】:
【参考方案11】:另一种选择是使用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
即可使用。
【讨论】:
【参考方案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
虽然这不会返回任何内容,但您应该使用多处理池模块,您可以在此处找到:how to get the return value from a thread in python?
【讨论】:
第一行不应该是:t = threading.Thread(target=sys.stdin.read, args=(1,)) 这个解决方案不会总是休眠 5 秒,即使用户在此之前按下了一个键?以上是关于在python中轮询键盘(检测按键)的主要内容,如果未能解决你的问题,请参考以下文章