如何用Python案例实现全局热键?

wen python案例 2

本文目录导读:

如何用Python案例实现全局热键?

  1. 方法一:使用 keyboard 库(最简单)
  2. 方法二:使用 pynput 库(更专业)
  3. 方法三:使用 ctypes(Windows原生API)
  4. 方法四:使用万能的热键管理器
  5. 安装依赖
  6. 使用建议

我来介绍几种实现全局热键的Python方法,并提供完整的案例。

使用 keyboard 库(最简单)

import keyboard
import time
class GlobalHotkey:
    def __init__(self):
        self.is_running = False
    def on_ctrl_shift_a(self):
        print("按下: Ctrl+Shift+A - 执行操作A")
    def on_ctrl_shift_b(self):
        print("按下: Ctrl+Shift+B - 执行操作B")
    def on_esc(self):
        print("按下: ESC - 退出程序")
        self.is_running = False
        return False  # 停止监听
    def start(self):
        # 注册热键
        keyboard.add_hotkey('ctrl+shift+a', self.on_ctrl_shift_a)
        keyboard.add_hotkey('ctrl+shift+b', self.on_ctrl_shift_b)
        keyboard.add_hotkey('esc', self.on_esc)
        print("全局热键启动成功!")
        print("Ctrl+Shift+A: 执行操作A")
        print("Ctrl+Shift+B: 执行操作B")
        print("ESC: 退出程序")
        print("按下 Ctrl+C 也可以退出...")
        self.is_running = True
        try:
            keyboard.wait()  # 等待按键
        except KeyboardInterrupt:
            pass
        finally:
            keyboard.unhook_all()
# 使用示例
if __name__ == "__main__":
    hotkey = GlobalHotkey()
    hotkey.start()

使用 pynput 库(更专业)

from pynput import keyboard
from pynput.keyboard import Key, Controller
import threading
import time
class PynputHotkey:
    def __init__(self):
        self.current_keys = set()
        self.hotkeys = {
            frozenset({Key.ctrl, Key.shift, keyboard.KeyCode.from_char('a')}): self.action_a,
            frozenset({Key.ctrl, Key.shift, keyboard.KeyCode.from_char('b')}): self.action_b,
            frozenset({Key.ctrl, Key.alt, keyboard.KeyCode.from_char('c')}): self.action_c,
        }
        self.key_controller = Controller()
    def action_a(self):
        print("执行操作A: 复制选中内容")
        # 模拟Ctrl+C
        with self.key_controller.pressed(Key.ctrl):
            self.key_controller.press('c')
            self.key_controller.release('c')
    def action_b(self):
        print("执行操作B: 粘贴内容")
        # 模拟Ctrl+V
        with self.key_controller.pressed(Key.ctrl):
            self.key_controller.press('v')
            self.key_controller.release('v')
    def action_c(self):
        print("执行操作C: 显示通知")
        # 这里可以执行任何操作
        self.show_notification()
    def show_notification(self):
        try:
            from plyer import notification
            notification.notify(
                title="全局热键",
                message="执行操作C成功!",
                timeout=3
            )
        except ImportError:
            print("提示: 如需显示系统通知,请安装plyer库")
    def on_press(self, key):
        try:
            self.current_keys.add(key)
            # 检查是否匹配任何热键组合
            for hotkey_set in self.hotkeys:
                if hotkey_set.issubset(self.current_keys):
                    self.hotkeys[hotkey_set]()
                    self.current_keys.clear()  # 防止重复触发
                    break
        except Exception as e:
            print(f"按键处理错误: {e}")
    def on_release(self, key):
        try:
            if key in self.current_keys:
                self.current_keys.remove(key)
            # 退出条件
            if key == Key.esc:
                print("ESC按下,退出程序...")
                return False
        except Exception as e:
            print(f"释放按键错误: {e}")
    def start(self):
        print("全局热键启动成功!")
        print("Ctrl+Shift+A: 复制")
        print("Ctrl+Shift+B: 粘贴")
        print("Ctrl+Alt+C: 显示通知")
        print("ESC: 退出程序")
        # 启动监听器
        with keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        ) as listener:
            listener.join()
# 使用示例
if __name__ == "__main__":
    hotkey = PynputHotkey()
    hotkey.start()

使用 ctypes(Windows原生API)

import ctypes
from ctypes import wintypes
import threading
import sys
class WindowsGlobalHotkey:
    # Windows API 常量
    WM_HOTKEY = 0x0312
    MOD_ALT = 0x0001
    MOD_CONTROL = 0x0002
    MOD_SHIFT = 0x0004
    MOD_WIN = 0x0008
    def __init__(self):
        self.hotkeys = {}
        self.running = True
        # 加载user32.dll
        self.user32 = ctypes.windll.user32
        # 获取消息函数
        self.GetMessage = self.user32.GetMessageW
        self.GetMessage.argtypes = [
            ctypes.POINTER(ctypes.wintypes.MSG),
            wintypes.HWND,
            wintypes.UINT,
            wintypes.UINT
        ]
        self.GetMessage.restype = ctypes.wintypes.BOOL
    def register_hotkey(self, hotkey_id, modifiers, vk_code, callback):
        """
        注册全局热键
        :param hotkey_id: 热键ID
        :param modifiers: 修饰键组合 (MOD_ALT, MOD_CONTROL, MOD_SHIFT, MOD_WIN)
        :param vk_code: 虚拟键码
        :param callback: 回调函数
        """
        result = self.user32.RegisterHotKey(
            None,  # hWnd
            hotkey_id,  # id
            modifiers,  # fsModifiers
            vk_code  # vk
        )
        if result:
            self.hotkeys[hotkey_id] = callback
            print(f"热键 {hotkey_id} 注册成功")
        else:
            print(f"热键 {hotkey_id} 注册失败")
    def unregister_all(self):
        """注销所有热键"""
        for hotkey_id in self.hotkeys:
            self.user32.UnregisterHotKey(None, hotkey_id)
        self.hotkeys.clear()
    def start(self):
        """开始监听热键"""
        print("Windows全局热键启动成功!")
        # 创建消息结构体
        msg = ctypes.wintypes.MSG()
        while self.running:
            # 获取消息
            ret = self.GetMessage(
                ctypes.byref(msg),
                None,
                0,
                0
            )
            if ret == 0:  # WM_QUIT
                break
            elif ret == -1:  # 错误
                print("消息获取错误")
                break
            # 处理热键消息
            if msg.message == self.WM_HOTKEY:
                hotkey_id = msg.wParam
                if hotkey_id in self.hotkeys:
                    try:
                        self.hotkeys[hotkey_id]()
                    except Exception as e:
                        print(f"热键执行错误: {e}")
    def stop(self):
        """停止监听"""
        self.running = False
        # 发送退出消息
        self.user32.PostQuitMessage(0)
        self.unregister_all()
# 热键操作类
class HotkeyActions:
    @staticmethod
    def action_1():
        print("执行操作1: Ctrl+Shift+A")
    @staticmethod
    def action_2():
        print("执行操作2: Ctrl+Shift+B")
    @staticmethod
    def action_3():
        print("执行操作3: Ctrl+Alt+D")
    @staticmethod
    def exit_action():
        print("退出程序")
        sys.exit(0)
# 使用示例
if __name__ == "__main__":
    # Windows虚拟键码
    VK_A = 0x41
    VK_B = 0x42
    VK_D = 0x44
    VK_ESCAPE = 0x1B
    hotkey = WindowsGlobalHotkey()
    # 注册热键
    actions = HotkeyActions()
    # Ctrl+Shift+A
    hotkey.register_hotkey(
        1,
        WindowsGlobalHotkey.MOD_CONTROL | WindowsGlobalHotkey.MOD_SHIFT,
        VK_A,
        actions.action_1
    )
    # Ctrl+Shift+B
    hotkey.register_hotkey(
        2,
        WindowsGlobalHotkey.MOD_CONTROL | WindowsGlobalHotkey.MOD_SHIFT,
        VK_B,
        actions.action_2
    )
    # Ctrl+Alt+D
    hotkey.register_hotkey(
        3,
        WindowsGlobalHotkey.MOD_CONTROL | WindowsGlobalHotkey.MOD_ALT,
        VK_D,
        actions.action_3
    )
    # ESC退出
    hotkey.register_hotkey(
        4,
        0,
        VK_ESCAPE,
        actions.exit_action
    )
    try:
        hotkey.start()
    except KeyboardInterrupt:
        hotkey.stop()

使用万能的热键管理器

import sys
import threading
from typing import Dict, Callable, Set
from dataclasses import dataclass
from enum import Enum
class KeyModifier(Enum):
    NONE = 0
    CTRL = 1
    SHIFT = 2
    ALT = 3
    WIN = 4
@dataclass
class HotkeyConfig:
    """热键配置"""
    keys: tuple  # 按键组合
    modifiers: tuple  # 修饰键
    callback: Callable
    description: str = ""
class SmartHotkeyManager:
    """智能热键管理器"""
    def __init__(self):
        self.hotkeys: Dict[str, HotkeyConfig] = {}
        self.use_library = self._detect_best_library()
    def _detect_best_library(self) -> str:
        """检测最佳可用的库"""
        try:
            import keyboard
            return 'keyboard'
        except ImportError:
            try:
                import pynput
                return 'pynput'
            except ImportError:
                return 'fallback'
    def add_hotkey(self, name: str, keys: tuple, modifiers: tuple, 
                   callback: Callable, description: str = ""):
        """添加热键"""
        config = HotkeyConfig(
            keys=keys,
            modifiers=modifiers,
            callback=callback,
            description=description
        )
        self.hotkeys[name] = config
    def remove_hotkey(self, name: str):
        """移除热键"""
        if name in self.hotkeys:
            del self.hotkeys[name]
    def _format_hotkey(self, config: HotkeyConfig) -> str:
        """格式化热键显示"""
        parts = []
        for mod in config.modifiers:
            if mod == KeyModifier.CTRL:
                parts.append('Ctrl')
            elif mod == KeyModifier.SHIFT:
                parts.append('Shift')
            elif mod == KeyModifier.ALT:
                parts.append('Alt')
            elif mod == KeyModifier.WIN:
                parts.append('Win')
        for key in config.keys:
            parts.append(key.upper() if len(key) == 1 else key)
        return '+'.join(parts)
    def show_help(self):
        """显示帮助信息"""
        print("\n=== 全局热键帮助 ===")
        print(f"使用库: {self.use_library}")
        print("-" * 40)
        for name, config in self.hotkeys.items():
            hotkey_str = self._format_hotkey(config)
            desc = config.description or name
            print(f"{hotkey_str:20s} - {desc}")
        print("=" * 40)
    def start(self):
        """启动热键监听"""
        if self.use_library == 'keyboard':
            self._start_with_keyboard()
        elif self.use_library == 'pynput':
            self._start_with_pynput()
        else:
            print("警告: 未检测到keyboard或pynput库")
            print("请安装: pip install keyboard 或 pip install pynput")
    def _start_with_keyboard(self):
        """使用keyboard库启动"""
        import keyboard
        for name, config in self.hotkeys.items():
            hotkey_str = self._format_hotkey(config).lower()
            keyboard.add_hotkey(hotkey_str, config.callback)
        self.show_help()
        print("按 ESC 退出程序")
        try:
            keyboard.wait('esc')
        except KeyboardInterrupt:
            pass
        finally:
            keyboard.unhook_all()
    def _start_with_pynput(self):
        """使用pynput库启动"""
        from pynput import keyboard
        class PynputHandler:
            def __init__(self, hotkeys):
                self.hotkeys = hotkeys
                self.current_keys = set()
            def format_pynput_hotkey(self, config: HotkeyConfig) -> set:
                result = set()
                for mod in config.modifiers:
                    if mod == KeyModifier.CTRL:
                        result.add(keyboard.Key.ctrl)
                    elif mod == KeyModifier.SHIFT:
                        result.add(keyboard.Key.shift)
                    elif mod == KeyModifier.ALT:
                        result.add(keyboard.Key.alt)
                    elif mod == KeyModifier.WIN:
                        result.add(keyboard.Key.cmd)
                for key in config.keys:
                    if len(key) == 1:
                        result.add(keyboard.KeyCode.from_char(key))
                    else:
                        result.add(getattr(keyboard.Key, key.lower(), None))
                return result
            def on_press(self, key):
                try:
                    self.current_keys.add(key)
                    for config in self.hotkeys.values():
                        hotkey_set = self.format_pynput_hotkey(config)
                        if hotkey_set.issubset(self.current_keys):
                            config.callback()
                            self.current_keys.clear()
                            break
                except Exception as e:
                    print(f"错误: {e}")
            def on_release(self, key):
                try:
                    self.current_keys.discard(key)
                    if key == keyboard.Key.esc:
                        return False
                except:
                    pass
        handler = PynputHandler(self.hotkeys)
        self.show_help()
        with keyboard.Listener(
            on_press=handler.on_press,
            on_release=handler.on_release
        ) as listener:
            listener.join()
# 使用示例
if __name__ == "__main__":
    def action_copy():
        print("复制操作!")
    def action_paste():
        print("粘贴操作!")
    def action_custom():
        print("自定义操作!")
    # 创建热键管理器
    manager = SmartHotkeyManager()
    # 注册热键
    manager.add_hotkey(
        "copy",
        keys=('c',),
        modifiers=(KeyModifier.CTRL,),
        callback=action_copy,
        description="复制"
    )
    manager.add_hotkey(
        "paste",
        keys=('v',),
        modifiers=(KeyModifier.CTRL,),
        callback=action_paste,
        description="粘贴"
    )
    manager.add_hotkey(
        "custom",
        keys=('f1',),
        modifiers=(KeyModifier.CTRL, KeyModifier.SHIFT),
        callback=action_custom,
        description="自定义操作"
    )
    # 启动
    manager.start()

安装依赖

# 方法一 (推荐): keyboard
pip install keyboard
# 方法二: pynput
pip install pynput
# 可选: 系统通知
pip install plyer

使用建议

  1. 简单使用: 使用 keyboard 库,代码最简单
  2. 跨平台: 使用 pynput 库,支持Windows/Linux/Mac
  3. Windows专用: 使用Windows API,性能最好
  4. 通用方案: 使用智能管理器,自动选择最佳库

选择哪种方法取决于你的具体需求和应用场景。

抱歉,评论功能暂时关闭!