即使 Python 脚本没有焦点,也会监听快捷键(例如 WIN+A)

Bas*_*asj 8 python windows keyboard keyboard-shortcuts

我希望 Python 脚本能够在后台持续运行,并在按下某个键盘快捷键(例如WIN+ )时执行某些操作A

我读过Python中的Key Listeners?和解决方案使用pynput,但它似乎可以工作并在窗口/控制台具有焦点时检测按键。

问题:如何检测Python中的键盘快捷键,例如WIN+ A,并在发生时启动一个函数,即使焦点在其他地方(例如浏览器等)?

注意:我的操作系统是Windows。另外,我希望Python脚本只“注册”监听WIN+ A(这存在吗?),并且不监听所有按键(否则它或多或少会是一个键盘记录器,这是我不想要的!)。


这是我尝试过的:

import pyHook, pythoncom

def OnKeyboardEvent(event):
    if event.Ascii == 8:  # backspace 
        print('hello')
    return True
    
hm = pyHook.HookManager()
hm.KeyDown = OnKeyboardEvent
hm.HookKeyboard()
pythoncom.PumpMessages()
Run Code Online (Sandbox Code Playgroud)

我想避免它有两个原因:首先,我发现监听所有按键非常具有侵入性,其次,pyhook我在很多地方发现的这个常见示例有一个错误:TypeError: KeyboardSwitch() missing 8 required positional arguments: 'msg', 'vk_ code', 'scan_code', 'ascii', 'flags', 'time', 'hwnd', and 'win_name'。新版本的 PyHook3 似乎也不适用于 Py36-64:pip install PyHook3在 Windows 上失败。

Bas*_*asj 7

事实上,我错了:pynput确实能够全局检测按键,而不仅仅是当前活动窗口。

1)使用pynput

以下代码notepad使用WIN+Wcalc+WIN启动应用程序C

from pynput import keyboard
import subprocess

pressed = set()

COMBINATIONS = [
    {
        "keys": [
            {keyboard.Key.cmd, keyboard.KeyCode(char="w")},
            {keyboard.Key.cmd, keyboard.KeyCode(char="W")},
        ],
        "command": "notepad",
    },
    {
        "keys": [
            {keyboard.Key.cmd, keyboard.KeyCode(char="c")},
            {keyboard.Key.cmd, keyboard.KeyCode(char="C")},
        ],
        "command": "calc",
    },
]

def run(s):
    subprocess.Popen(s)

def on_press(key):
    pressed.add(key)
    print(pressed)
    for c in COMBINATIONS:
        for keys in c["keys"]:
            if keys.issubset(pressed):
                run(c["command"])

def on_release(key):
    if key in pressed:
        pressed.remove(key)

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()
Run Code Online (Sandbox Code Playgroud)

2)使用ctypeswin32con

这是受到 @fpbhb 在评论中引用的这篇文章的启发。它监听WIN+F3WIN+ F4

import os, sys
import ctypes
from ctypes import wintypes
import win32con

def handle_win_f3():
    os.startfile(os.environ["TEMP"])

def handle_win_f4():
    ctypes.windll.user32.PostQuitMessage(0)

HOTKEYS = [
    {"keys": (win32con.VK_F3, win32con.MOD_WIN), "command": handle_win_f3},
    {"keys": (win32con.VK_F4, win32con.MOD_WIN), "command": handle_win_f4},
]

for i, h in enumerate(HOTKEYS):
    vk, modifiers = h["keys"]
    print("Registering id", i, "for key", vk)
    if not ctypes.windll.user32.RegisterHotKey(None, i, modifiers, vk):
        print("Unable to register id", i)
try:
    msg = wintypes.MSG()
    while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
        if msg.message == win32con.WM_HOTKEY:
            HOTKEYS[msg.wParam]["command"]()
        ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
        ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
finally:
    for i, h in enumerate(HOTKEYS):
        ctypes.windll.user32.UnregisterHotKey(None, i)
Run Code Online (Sandbox Code Playgroud)

3)使用keyboard

请参阅https://github.com/boppreh/keyboard。这很有趣,但对我来说,与其他解决方案相比,它有一些缺点:一些快速击键(在 CPU 密集型处理完成时)没有被捕获。他们是在使用解决方案 1) 或 2) 时被抓住的。


似乎没有 100% Tkinter 解决方案,请参阅使用 Tkinter 捕获系统的所有按键