如何拦截在 Python 程序的终端 window 中(且仅在)中按下的键?
How to intercept keys pressed in (and only in) the terminal window of a Python program?
我正在编写一个简单的 Python 3 脚本,我希望能够拦截终端内的按键操作 window 以便根据返回值执行某些操作,例如。
我也想要一个跨平台的解决方案。
我想重现这样的东西:
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!")
但是根据 Python docs(和我的测试)msvcrt
是一个 Windows 特定的模块:
These functions provide access to some useful capabilities on Windows platforms.
我找到了 keyboard
模块,它使用起来非常简单(并且 more 跨平台)但我没能 "catch" 只有 在 终端 window 内按下的键 window.
例如:
import keyboard as kb
key = kb.read_hotkey()
if key == "esc": # The ESC key
print("You have pressed the ESC key!")
上面给出的代码不仅在执行脚本的终端 window 获得焦点时拦截按键,而且在没有获得焦点时也拦截。
因此,总而言之,您是否知道一种 pythonic 方式来拦截执行脚本的终端 window(而不是外部)内的按键(类似于 input()
而无需按 Enter),并且它是跨平台的(至少兼容 GNU/Linux 和 Windows)?
提前感谢您的回答,
问候,
亚历克西斯
这是适用于 Windows 的部分解决方案,也适用于 GNU/Linux。
我注意到在 GNU/Linux(至少在 Debian 9 上)相同的数字被分配给箭头键和 ESC 键。
下面的代码是受题解的启发
# 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!")
此致,
亚历克西斯
看看 curses 模块。它在 python 标准库中,但不支持开箱即用的 windows。
有一个定期维护的项目叫做 "windows-curses" 你可以看看。我没有测试过它,但它支持osedly 允许您在 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 方法对您不起作用,或者您仍然需要更细粒度,那么您可以相当轻松地推出自己的 cross-platform 方法。你可以尝试这样的事情:
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()
Keyboard.__new__ 的工作是在运行时为当前 os.
提供适当的解决方案
无论活动 window.
,此方法仍会注册 key-presses
为此,您需要访问活动的 window,这将是另一个 os-specific 过程。
看看这个:
您可以实现一个检查当前 window
名称的函数
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中实现适当的逻辑
这不会考虑在不同的选项卡中。这可能是possible,但我对api不够熟悉,无法告诉你。
还有 pygame 等选项需要您创建和管理自己的 windows 但会满足您的要求。
编辑:将 WinKeyboard 和 MacKeyboard 更改为从键盘继承。
我正在编写一个简单的 Python 3 脚本,我希望能够拦截终端内的按键操作 window 以便根据返回值执行某些操作,例如。
我也想要一个跨平台的解决方案。
我想重现这样的东西:
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!")
但是根据 Python docs(和我的测试)msvcrt
是一个 Windows 特定的模块:
These functions provide access to some useful capabilities on Windows platforms.
我找到了 keyboard
模块,它使用起来非常简单(并且 more 跨平台)但我没能 "catch" 只有 在 终端 window 内按下的键 window.
例如:
import keyboard as kb
key = kb.read_hotkey()
if key == "esc": # The ESC key
print("You have pressed the ESC key!")
上面给出的代码不仅在执行脚本的终端 window 获得焦点时拦截按键,而且在没有获得焦点时也拦截。
因此,总而言之,您是否知道一种 pythonic 方式来拦截执行脚本的终端 window(而不是外部)内的按键(类似于 input()
而无需按 Enter),并且它是跨平台的(至少兼容 GNU/Linux 和 Windows)?
提前感谢您的回答,
问候,
亚历克西斯
这是适用于 Windows 的部分解决方案,也适用于 GNU/Linux。
我注意到在 GNU/Linux(至少在 Debian 9 上)相同的数字被分配给箭头键和 ESC 键。
下面的代码是受
# 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!")
此致,
亚历克西斯
看看 curses 模块。它在 python 标准库中,但不支持开箱即用的 windows。 有一个定期维护的项目叫做 "windows-curses" 你可以看看。我没有测试过它,但它支持osedly 允许您在 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 方法对您不起作用,或者您仍然需要更细粒度,那么您可以相当轻松地推出自己的 cross-platform 方法。你可以尝试这样的事情:
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()
Keyboard.__new__ 的工作是在运行时为当前 os.
提供适当的解决方案无论活动 window.
,此方法仍会注册 key-presses
为此,您需要访问活动的 window,这将是另一个 os-specific 过程。
看看这个:
您可以实现一个检查当前 window
名称的函数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中实现适当的逻辑 这不会考虑在不同的选项卡中。这可能是possible,但我对api不够熟悉,无法告诉你。
还有 pygame 等选项需要您创建和管理自己的 windows 但会满足您的要求。
编辑:将 WinKeyboard 和 MacKeyboard 更改为从键盘继承。