shortcutkey:跨平台快捷键管理工具的设计与实现

1. 项目简介

shortcutkey是一个功能强大的跨平台快捷键管理工具,允许用户通过自定义快捷键快速打开、切换和管理指定的软件。它不仅支持基本的快捷键功能,还提供智能窗口管理、多语言界面和开机自启动等高级特性。本文将详细介绍该项目的设计思路、技术实现和核心功能模块。

2. 系统架构设计

项目采用模块化设计,主要由以下几个核心模块组成:

  1. AppManager :应用程序管理器,负责应用的打开、最小化和窗口状态检测
  2. HotkeyListener :快捷键监听器,处理全局快捷键的捕获和响应
  3. ConfigManager :配置管理器,处理配置文件的读取、保存和用户配置界面
  4. LangManager :语言管理器,提供多语言支持功能
  5. Autostart :开机自启动模块,支持Windows、macOS和Linux系统
  6. Logger :日志记录模块,提供详细的运行日志 这种模块化设计使得代码结构清晰,易于维护和扩展。各模块之间通过明确的接口进行交互,降低了耦合度。

3. 核心功能实现

3.1. 全局快捷键监听

快捷键监听是项目的核心功能,使用 pynput 库实现。该模块采用单例模式设计,确保全局只有一个监听器实例:

Python 复制代码
class HotkeyListener:
    """快捷键监听器"""
    
    # 单例模式实现
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls, *args, **kwargs):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super(HotkeyListener, cls).__new__(cls)
        return cls._instance

快捷键解析函数支持两种格式:单个字符格式和完整格式(修饰键+shift+字母):

Python 复制代码
def _parse_hotkeys(self):
    """解析快捷键配置"""
    shortcuts = self.config.get('shortcuts', [])
    
    for shortcut in shortcuts:
        try:
            hotkey_str = shortcut['hotkey']
            app_path = shortcut.get('location', shortcut.get('path'))
            
            # 解析快捷键字符串
            key_parts = hotkey_str.split('+')
            keys = set()
            
            # 支持两种格式
            if len(key_parts) == 1 and len(hotkey_str.strip()) == 1 and hotkey_str.strip().isalpha():
                # 自动添加ctrl和shift键
                keys.add(keyboard.Key.ctrl)
                keys.add(keyboard.Key.shift)
                keys.add(hotkey_str.strip().lower())
            else:
                # 处理完整格式
                # ...

按键检测采用精确匹配策略,确保只有当按下的按键集合与配置的快捷键完全匹配时才触发操作:

Python 复制代码
def _on_press(self, key):
    """按键按下时的处理"""
    try:
        # 将按键添加到当前按键集合
        if hasattr(key, 'char') and key.char:
            self.current_keys.add(key.char.lower())
        else:
            self.current_keys.add(key)
        
        # 检查是否匹配某个快捷键
        current_set = frozenset(self.current_keys)
        
        # 只有当当前按键集合恰好等于快捷键集合时才触发
        for shortcut_set, app_location in self.hotkeys.items():
            if current_set == shortcut_set:
                self.app_manager.open_app(app_location)
                break
    except Exception as e:
        logger.error(f"按键处理错误: {str(e)}")

3.2. 智能窗口管理

智能窗口管理功能允许程序检测应用是否在前台,如果是则将其最小化,实现快速切换:

Python 复制代码
def _is_app_in_foreground(self, app_path):
    """检查应用是否在前台"""
    try:
        if platform.system() == 'Darwin':  # macOS
            # 使用AppleScript检查前台应用
            app_name = os.path.basename(app_path)
            if app_name.endswith('.app'):
                app_name = app_name[:-4]
            
            script = f"tell application \"System Events\" to get name of first application process whose frontmost is true"
            result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True)
            frontmost_app = result.stdout.strip()
            
            return frontmost_app.lower() == app_name.lower()
        
        elif platform.system() == 'Windows':  # Windows
            # 使用win32gui获取当前活动窗口
            # ...

这个功能需要针对不同操作系统使用不同的API实现,项目通过 platform 模块进行平台检测,确保在各操作系统上都能正常工作。

3.3. 交互式配置界面

配置界面支持搜索和分页浏览应用列表,用户体验得到极大提升:

Python 复制代码
# 分页显示应用列表
app_choice = input(_('configure_hotkeys.enter_app_choice')).strip()

try:
    app_index = int(app_choice) - 1
    
    if app_index == len(apps):  # 手动输入应用路径
        app_location = input(_('configure_hotkeys.enter_app_path')).strip()
        app_name = os.path.basename(app_location)
        break
    elif app_index == len(apps) + 1:  # 上一页
        if current_batch > 0:
            current_batch -= 1
        continue
    elif app_index == len(apps) + 2:  # 下一页
        if end < len(apps):
            current_batch += 1
        continue
    elif 0 <= app_index < len(apps):
        app_location = apps[app_index]['location']
        app_name = apps[app_index]['name']
        break

特别优化了应用选择界面,支持上下箭头键快速切换页面,提高操作效率。

3.4. 多语言支持

项目实现了简洁而强大的语言管理器,支持配置文件驱动的多语言切换:

Python 复制代码
class LangManager:
    """简单的语言管理器"""
    
    def __init__(self):
        self.language = 'zh'
        self.translations = {}
        self.language_file_path = None
        self._init_language_file()
        self._load_translations()
    
    def get_text(self, key_path, **kwargs):
        """获取翻译文本"""
        keys = key_path.split('.')
        text = self.translations.get(self.language, {})
        for key in keys:
            if isinstance(text, dict) and key in text:
                text = text[key]
            else:
                return key_path
        
        if isinstance(text, str):
            try:
                return text.format(**kwargs)
            except (KeyError, IndexError):
                pass
        
        return text

# 全局翻译函数
_ = _lang_manager.get_text

语言配置存储在YAML文件中,支持嵌套的键路径和参数化字符串。

3.5. 跨平台开机自启动

项目为Windows、macOS和Linux提供了不同的开机自启动实现方式:

  • Windows :创建注册表项
  • macOS :创建Launch Agent配置文件
  • Linux :创建桌面条目并添加到自启动文件夹 同时处理了各种错误情况,确保在权限不足或配置错误时能够给出明确的提示。

4. 技术要点与优化

4.1. 权限管理与用户体验

在macOS系统上,应用需要辅助功能权限才能监听全局快捷键。项目实现了自动权限检测和引导功能:

Python 复制代码
def _check_permissions(self):
    """检查是否有必要的系统权限"""
    if platform.system() == 'Darwin':  # macOS
        try:
            # 尝试创建临时监听器测试权限
            temp_listener = keyboard.Listener()
            temp_listener.start()
            temp_listener.stop()
            temp_listener.join(timeout=1)
            return True
        except Exception:
            print("重要提示: 该程序需要辅助功能权限才能监听键盘输入")
            print("正在自动打开系统偏好设置的辅助功能页面...")
            # 自动打开系统设置
            subprocess.run(["open", "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"])
            # 提供详细的授权步骤指导
            # ...
            return False

4.2. 异常处理与日志记录

项目实现了完善的异常处理机制和日志系统,便于问题排查:

Python 复制代码
class Logger:
    """日志记录器类"""
    
    def __init__(self, name="shortcutkey", level=logging.INFO):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(level)
        
        # 创建日志目录和文件
        user_home = os.path.expanduser("~")
        log_dir = os.path.join(user_home, ".shortcutkey/log")
        if not os.path.exists(log_dir):
            try:
                os.makedirs(log_dir)
            except Exception as e:
                print(f"创建日志目录失败: {str(e)}")
                return
        
        # 按日期创建日志文件
        today = datetime.datetime.now().strftime("%Y-%m-%d")
        log_file = os.path.join(log_dir, f"shortcutkey_{today}.log")
        
        # 设置日志格式
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(formatter)
        
        if not self.logger.handlers:
            self.logger.addHandler(file_handler)

4.3. 进程单例与状态管理

为了避免程序重复运行,项目实现了进程单例机制,通过PID文件来检查和管理进程状态:

Python 复制代码
def _is_program_running(self):
    """检查程序是否已经在运行"""
    try:
        if os.path.exists(self.pid_file):
            with open(self.pid_file, 'r') as f:
                pid = int(f.read().strip())
            
            # 检查进程是否存在
            if platform.system() == 'Windows':
                # Windows系统检查进程
                import psutil
                return psutil.pid_exists(pid)
            else:
                # Unix系统检查进程
                try:
                    os.kill(pid, 0)
                    return True
                except OSError:
                    return False
    except Exception:
        pass
    return False

5. 项目部署与打包

项目使用了多种打包工具和脚本来实现应用程序的构建、分发和发布。下面详细介绍各个打包脚本的功能和实现方式。

5.1. PyInstaller打包配置 (shortcutkey.spec)

这是使用PyInstaller进行应用打包的配置文件,用于创建独立的可执行文件:

Python 复制代码
# -*- mode: python ; coding: utf-8 -*-

# 分析阶段:指定源文件、依赖等
a = Analysis(
    ['shortcutkey/main.py'],  # 主入口文件
    pathex=[],
    binaries=[],
    datas=[('shortcutkey/config/default_config.yaml', 'shortcutkey/config')],  # 包含配置文件
    hiddenimports=['yaml', 'pkg_resources'],  # 显式指定的隐藏导入
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)  # 创建Python字节码存档

# 创建可执行文件
exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='shortcutkey',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,  # 使用UPX压缩
    console=True,  # 使用控制台模式
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

# 收集所有依赖文件到输出目录
coll = COLLECT(
    exe,
    a.binaries,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='shortcutkey',
)

主要功能 :

  • 指定主入口文件为 shortcutkey/main.py
  • 包含配置文件 default_config.yaml
  • 设置必要的隐藏导入( yaml 和 pkg_resources )
  • 配置UPX压缩以减小可执行文件体积
  • 创建控制台模式的可执行文件

5.2. Python包安装配置 (setup.py)

这是Python包的标准安装配置文件,定义了包的元数据和安装行为:

Python 复制代码
setup(
    name="shortcutkey",
    version=__version__,  # 从__init__.py导入版本号
    author="Alex",
    author_email="664141154@qq.com",
    description="通过快捷键打开指定软件的工具",
    long_description=long_description,  # 从README.md读取详细描述
    long_description_content_type="text/markdown",
    url="https://github.com/ZBIGBEAR/shortcutkey",
    packages=find_packages(),  # 自动发现所有包
    classifiers=[  # 分类标签
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.6',  # Python版本要求
    install_requires=[  # 依赖项
        "pynput>=1.7.6",
        "pyyaml>=6.0",
        "pyinstaller>=5.0.0",
    ],
    entry_points={  # 控制台脚本入口
        'console_scripts': [
            'shortcutkey=shortcutkey.main:main',
        ],
    },
    include_package_data=True,
    package_data={  # 包含非代码文件
        'shortcutkey': ['config/*.yaml'],
    },
)

关键特性 :

  • init.py 导入版本号,保持版本一致性
  • 使用README.md作为长描述
  • 定义控制台脚本入口 shortcutkey ,指向 shortcutkey.main:main 函数
  • 包含配置文件等非代码资源
  • 指定依赖项及其版本要求

5.3. PyPI发布自动化脚本 (publish_package.sh)

这是一个功能强大的Bash脚本,用于自动化版本升级、构建和发布到PyPI的过程:

核心功能 :

  1. 版本号管理 :
Bash 复制代码
# 版本号递增函数
increment_version() {
    local version=$1
    local part=$2
    
    # 分割版本号
    local major minor patch
    IFS='.' read -r major minor patch <<< "$version"
    
    # 根据指定的部分递增版本号
    case $part in
        major) major=$((major + 1)); minor=0; patch=0 ;;
        minor) minor=$((minor + 1)); patch=0 ;;
        *) patch=$((patch + 1)) ;;
    esac
    
    echo "$major.$minor.$patch"
}
  1. 版本号更新 :自动从 init.py 读取当前版本,并根据参数(major/minor/patch)递增版本号

  2. 构建流程 :

  • 清理旧的构建文件
  • 使用 setup.py 构建sdist(源码分发包)和bdist_wheel(二进制wheel包)
  1. 智能发布 :支持发布到测试PyPI或正式PyPI
Bash 复制代码
if [ "$1" = "正式" ] || [ "$1" = "prod" ] || [ "$1" = "production" ]; then
    PUBLISH_URL=""
else
    PUBLISH_URL="--repository testpypi"
fi
  1. 完整的环境检查 :验证Python和pip版本,安装必要的打包工具
Bash 复制代码
./publish_package.sh [测试/正式] [版本号类型]
  • 第一个参数:指定发布到测试环境还是正式环境
  • 第二个参数:指定版本号递增类型(patch/minor/major),默认为patch

5.4. 打包发布工作流

项目的完整打包发布工作流程如下:

  1. 版本升级 :运行 publish_package.sh 脚本,自动更新版本号
  2. 源码打包 :通过 setup.py 创建源码包和wheel包
  3. 可执行文件打包 :使用PyInstaller( shortcutkey.spec )创建独立可执行文件
  4. 发布到PyPI :使用twine上传到测试或正式PyPI 这种多工具组合的方式既支持通过pip安装(适合开发者和高级用户),又提供了独立可执行文件(适合普通用户),大大提高了项目的可用性和分发效率。

6. 总结与展望

shortcutkey项目展示了如何使用Python构建一个功能完善的跨平台工具。通过模块化设计、精心的异常处理和用户体验优化,实现了一个易用、稳定的快捷键管理解决方案。

项目的主要亮点包括:

  1. 优秀的架构设计 :清晰的模块化结构,便于维护和扩展
  2. 完善的跨平台支持 :针对不同操作系统提供专门的实现
  3. 智能的窗口管理 :不仅仅是打开应用,还能智能切换窗口状态
  4. 友好的用户体验 :交互式配置、多语言支持、权限引导等
  5. 健壮的异常处理 :全面的错误捕获和日志记录

未来可以考虑添加更多高级功能,如:

  • GUI界面支持
  • 更复杂的快捷键组合
  • 窗口位置记忆和恢复
  • 云同步配置
  • 插件扩展系统

7. 项目链接

如果您对这个项目感兴趣,欢迎访问我的GitHub仓库:

github.com/ZBIGBEAR/sh...

请给项目点个Star支持一下,您的支持是我持续改进的动力!

也欢迎提交Issue和Pull Request,一起让这个工具变得更好。

相关推荐
攻城狮杰森43 分钟前
Eudic → Maimemo 自动同步工具:欧路词典 & 墨墨背单词
python·api·shell·ai编程·欧路词典·墨墨本单词
Vince的修炼之路1 小时前
用Python将JSON格式文件数据导入到Elasticsearch上
python
不会吉他的肌肉男不是好的挨踢男1 小时前
LLaMA Factory 训练模型未检测到CUDA环境解决
python·ai·llama
爱打球的大包包1 小时前
Deepseek-OCR部署与调用
github
Justinyh1 小时前
Notion同步到CSDN + 构建Obsidian本地博客系统指南
python·csdn·图床·notion·obsidian·文档同步·piclist
晚霞的不甘1 小时前
Flutter 与开源鸿蒙(OpenHarmony)性能调优与生产部署实战:从启动加速到线上监控的全链路优化
flutter·开源·harmonyos
D***y2011 小时前
【Python】网络爬虫——词云wordcloud详细教程,爬取豆瓣最新评论并生成各式词云
爬虫·python·信息可视化
疯不皮1 小时前
tiptiap3如何实现编辑器内部嵌套多个富文本编辑器
前端·vue.js·开源
后台开发者Ethan1 小时前
py文件被初始化执行了2次
python