如何截获(仅)在Python程序的终端窗口中按下的键?

时间:2019-07-08 17:33:33

标签: python keyboard keyboard-events

我正在编写一个简单的Python 3脚本,并且希望能够拦截终端窗口内的按键,以便根据返回的值执行某些操作。例如,
我也想要一个跨平台的解决方案。

我想重现这样的东西:

import msvcrt
key = ord(msvcrt.getch()) # Wait for a key to be pressed.
if key == 27: # The ESC key
    print("You have pressed the ESC key!")

但是msvcrt是Windows专用的模块,根据Python docs(和我的测试):

  

这些功能提供对Windows平台上某些有用功能的访问。

我找到了keyboard模块,该模块非常易于使用(并且 more 跨平台),但是我没有设法仅捕获按下的键>在终端窗口内部

例如:

import keyboard as kb
key = kb.read_hotkey()
if key == "esc": # The ESC key
    print("You have pressed the ESC key!")

上面给出的代码不仅在执行脚本的终端窗口聚焦时拦截按键,而且在没有聚焦时也拦截按键。

因此,总而言之,您是否知道一种在执行脚本的终端窗口内(而不是外部)截取按键的有效方式(类似于 input(),而无需按Enter < / em>),以及哪个是跨平台的(至少与GNU / Linux和Windows兼容)?

预先感谢您的回答,
问候,
亚历克西斯。

2 个答案:

答案 0 :(得分:0)

这是部分解决方案,可以在Windows上使用,并且可以在GNU / Linux上使用。
我注意到,在GNU / Linux(至少在Debian 9上)上,相同的数字被分配给箭头键和ESC键。

对于以下代码,我受this主题解决方案的启发。

# coding: utf8
import sys


def read() -> int:
    if sys.platform == "win32":
        import msvcrt
        key = ord(msvcrt.getch())  # Wait for a key to be pressed.
    elif sys.platform == "linux":
        import tty
        import termios
        try:
            orig_settings = termios.tcgetattr(sys.stdin)
            tty.setcbreak(sys.stdin)
            key = ord(sys.stdin.read(1)[0])
        except KeyboardInterrupt:
            key = 3 # The code for Ctrl+C got on Windows.
        finally:  # To make sure that the terminal will return to its original state.
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
    else:
        raise RuntimeError("Your platform is not supported")
    return key

if read() == 27:  # The ESC key (also the UP-DOWN-RIGHT-LEFT on GNU/Linux)
    print("You have pressed the ESC key!")

致谢,
亚历克西斯。

答案 1 :(得分:0)

看看curses模块。它在python标准库中,但不支持现成的Windows。 您可以查看一个定期维护的名为“ windows-curses”的项目。我尚未对其进行测试,但是据说它可以让您在Windows上使用python curses模块。 https://pypi.org/project/windows-curses/

import curses

def listen(window):
    while True:
        key = window.getch()
        window.addstr(f'You pressed the "{key}" key!\n')
        if key == 'q':
            break
        handle_keypress(key)

curses.wrapper(listen)

如果curses方法对您不起作用,或者您仍然需要更多粒度,则可以相当轻松地推出自己的跨平台方法。您可以尝试这样的事情:

from sys import platform

class Keyboard:
    def __new__(cls):
        if platform in ['Windows', 'win32', 'cygwin']:
            cls = winKeyboard
        elif platform in ['Mac', 'darwin', 'os2', 'os2emx']:
            cls = MacKeyboard
        else:
            raise Exception(f'Unrecognized platform, {platform}')
        return super(Keyboard, cls).__new__(cls)

    def listen(self):
        while True:
            key = self.getch()
            print(f'You pressed the "{key}" key!')
            if key == 27:
                break
            return self.handle_key(key)

class MacKeyboard(Keyboard):
    def getch(self):
        implement_mac_logic()


class WinKeyboard(Keyboard):
    def getch(self):
        implement_win_logic()

keyboard = Keyboard()
keyboard.listen()

键盘。__new__会在运行时为当前操作系统提供适当的解决方案。

无论活动窗口如何,此方法仍将注册按键。
为此,您将需要访问活动窗口,这是另一个特定于OS的过程。 看看这个:https://stackoverflow.com/a/36419702/1420455

您可以实现检查当前窗口名称的功能

class Keyboard:
    ...
    def listen(self):
        while True:
            if self.get_active_window() != desired_window:
                continue
            key = self.getch()
            print(f'You pressed the "{key}" key!')
            if key == 27:
                break
            return self.handle_key(key)

然后,您可以只在WinKeyboard.get_active_window和MacKeyboard.get_active_window中实现适当的逻辑 这不会考虑到在不同的选项卡中。这可能是可行的,但是我对这些API并不熟悉,无法告诉您。

还有诸如pygame之类的选项,它们将要求您创建和管理自己的窗口,但将满足您的要求。

编辑:将WinKeyboard和MacKeyboard更改为从Keyboard继承。