74

如何从控制台 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 上执行此操作的正确 pythonic 方法是什么?此外,Linux 的可移植性也不错,尽管它不是必需的。

4

12 回答 12

39

标准方法是使用select模块。

但是,这在 Windows 上不起作用。为此,您可以使用msvcrt模块的键盘轮询。

通常,这是通过多个线程来完成的——每个设备一个被“监视”加上可能需要被设备中断的后台进程。

于 2008-11-15T17:09:00.650 回答
22

使用 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)
于 2010-08-19T17:12:50.163 回答
17

好的,由于我尝试在评论中发布我的解决方案失败了,这就是我想说的。我可以使用以下代码从本机 Python(在 Windows 上,但在其他任何地方都没有)做我想要的事情:

import msvcrt 

def kbfunc(): 
   x = msvcrt.kbhit()
   if x: 
      ret = ord(msvcrt.getch()) 
   else: 
      ret = 0 
   return ret
于 2008-11-20T00:32:05.310 回答
16
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
于 2009-09-20T01:47:23.303 回答
16

这些答案都不适合我。这个包,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()
于 2016-12-11T06:31:18.320 回答
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)
于 2008-11-15T23:53:49.057 回答
5

kbhithttp://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,直到你这样做!

于 2019-04-15T15:13:56.460 回答
4

你可以看看pygame如何处理这个来窃取一些想法。

于 2008-11-15T03:49:45.193 回答
2

我用它来检查按键,再简单不过了:

#!/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 上运行,但我无法让它工作

于 2018-03-19T15:34:29.197 回答
1

这可以使用 python 中的“pynput”模块来完成,你按下一个键,它就会被打印出来。就这么简单!

  1. PIP 在命令提示符下安装模块,输入以下文本并按回车

    pip install pynput

  2. 运行以下代码:

    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()
    
  3. Key.enter您可以通过在代码的第 8 行更改为其他键来使用您想要的任何键结束循环。

于 2021-08-28T07:14:18.277 回答
1

另一种选择是使用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地使用它。

于 2021-10-27T14:22:43.990 回答
-1

如果结合 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 中的线程获取返回值?

于 2015-07-31T20:18:05.790 回答