1. 项目简介
shortcutkey是一个功能强大的跨平台快捷键管理工具,允许用户通过自定义快捷键快速打开、切换和管理指定的软件。它不仅支持基本的快捷键功能,还提供智能窗口管理、多语言界面和开机自启动等高级特性。本文将详细介绍该项目的设计思路、技术实现和核心功能模块。
2. 系统架构设计
项目采用模块化设计,主要由以下几个核心模块组成:
- AppManager :应用程序管理器,负责应用的打开、最小化和窗口状态检测
- HotkeyListener :快捷键监听器,处理全局快捷键的捕获和响应
- ConfigManager :配置管理器,处理配置文件的读取、保存和用户配置界面
- LangManager :语言管理器,提供多语言支持功能
- Autostart :开机自启动模块,支持Windows、macOS和Linux系统
- 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的过程:
核心功能 :
- 版本号管理 :
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"
}
-
版本号更新 :自动从 init.py 读取当前版本,并根据参数(major/minor/patch)递增版本号
-
构建流程 :
- 清理旧的构建文件
- 使用 setup.py 构建sdist(源码分发包)和bdist_wheel(二进制wheel包)
- 智能发布 :支持发布到测试PyPI或正式PyPI
Bash
if [ "$1" = "正式" ] || [ "$1" = "prod" ] || [ "$1" = "production" ]; then
PUBLISH_URL=""
else
PUBLISH_URL="--repository testpypi"
fi
- 完整的环境检查 :验证Python和pip版本,安装必要的打包工具
Bash
./publish_package.sh [测试/正式] [版本号类型]
- 第一个参数:指定发布到测试环境还是正式环境
- 第二个参数:指定版本号递增类型(patch/minor/major),默认为patch
5.4. 打包发布工作流
项目的完整打包发布工作流程如下:
- 版本升级 :运行 publish_package.sh 脚本,自动更新版本号
- 源码打包 :通过 setup.py 创建源码包和wheel包
- 可执行文件打包 :使用PyInstaller( shortcutkey.spec )创建独立可执行文件
- 发布到PyPI :使用twine上传到测试或正式PyPI 这种多工具组合的方式既支持通过pip安装(适合开发者和高级用户),又提供了独立可执行文件(适合普通用户),大大提高了项目的可用性和分发效率。
6. 总结与展望
shortcutkey项目展示了如何使用Python构建一个功能完善的跨平台工具。通过模块化设计、精心的异常处理和用户体验优化,实现了一个易用、稳定的快捷键管理解决方案。
项目的主要亮点包括:
- 优秀的架构设计 :清晰的模块化结构,便于维护和扩展
- 完善的跨平台支持 :针对不同操作系统提供专门的实现
- 智能的窗口管理 :不仅仅是打开应用,还能智能切换窗口状态
- 友好的用户体验 :交互式配置、多语言支持、权限引导等
- 健壮的异常处理 :全面的错误捕获和日志记录
未来可以考虑添加更多高级功能,如:
- GUI界面支持
- 更复杂的快捷键组合
- 窗口位置记忆和恢复
- 云同步配置
- 插件扩展系统
7. 项目链接
如果您对这个项目感兴趣,欢迎访问我的GitHub仓库:
请给项目点个Star支持一下,您的支持是我持续改进的动力!
也欢迎提交Issue和Pull Request,一起让这个工具变得更好。