资讯动态

Easy-Tools v0.3.0:一个用 ctypes + PySide6 写的 Windows 桌面效率工具(中键动作面板 / 事件链 / 本地 OCR)

发布时间:2026/10/24 10:55:38 来源:尧图企业网站定制
Easy-Tools v0.3.0一个用 ctypes PySide6 写的 Windows 桌面效率工具中键动作面板 / 事件链 / 本地 OCR源码https://github.com/FortC/Easy-Tools LicenseApache-2.0 版本v0.3.0技术栈Python 3 PySide6 纯 ctypes Win32 llama.cpp 代码量约 11.5k 行 / 26 个测试文件下载地址一、写在前面为什么又造一个轮子QWin、Quicker、AutoHotkey、Power Automate 都已经是成熟方案这个项目并不比它们功能多。本文写给想自己实现一个桌面动作面板或者想看一个完整桌面工具从 PRD 到发布的全过程的开发者。Easy-Tools 的定位很窄但目标明确设计约束具体做法触发要零成本鼠标中键/侧键全局钩子短按呼出、拖动保持原生滚动面板要认上下文两级识别进程名/标题正则L1零成本 本地 OCRL2带防抖缓存能力要可编排事件链 触发器 → 条件 → 动作序列的 JSON 流水线数据要留在本机内置 GGUF 模型本地推理OCR 可选依赖云端调用需显式选择依赖要少Win32 接入层不用 pywin32 / pynput全部用标准库 ctypes二、目录结构easy-tools/ ├── main.py # 入口托盘、单实例、初始化顺序 ├── easy_tools/ │ ├── core/ │ │ ├── app.py # 应用生命周期、托盘、GUI 线程队列 │ │ ├── chain.py # 事件链引擎节点调度、变量作用域 │ │ ├── context.py # 上下文识别L1 进程/标题 L2 OCR 融合 │ │ ├── ocr.py # OCR 服务后台线程、防抖、结果缓存 │ │ ├── config_store.py # 配置读写、保存即生效 │ │ ├── question_bank.py # Excel 题库解析、列映射、检索索引 │ │ ├── builtin_model.py # 内置 GGUF 模型封装 │ │ ├── ccswitch.py # 外部模型配置导入 │ │ ├── update_client.py # 更新检查 │ │ └── actions/ │ │ ├── base.py # 动作基类 注册机制 │ │ ├── openers.py # 打开应用/文件/URL/命令 │ │ ├── text_input.py # 文本投递粘贴 / 逐字键入 │ │ ├── clipboard.py # 剪贴板动作 │ │ └── processors.py # 40 可复用处理器同时是链步骤 │ ├── win32/ # ★ 纯 ctypes 接入层 │ │ ├── mouse.py # WH_MOUSE_LL 低级钩子、按键判定 │ │ ├── input.py # SendInput 合成按键/文本 │ │ ├── window.py # 前台窗口、进程名、标题 │ │ ├── clipboard.py # 剪贴板读写 │ │ ├── gdi.py # 屏幕/窗口位图抓取 │ │ └── api.py # 结构体声明、错误码翻译 │ ├── ui/ │ │ ├── panel.py # 动作面板搜索/Tab/分页/定位 │ │ ├── settings.py # 配置中心14 页侧边导航 │ │ ├── chain_editor.py # 事件链可视化编辑器 │ │ ├── question_bank_page.py │ │ ├── capture_overlay.py # 框选遮罩层 │ │ ├── widgets.py # 自绘控件 │ │ ├── theme.py # 主题与 QSS │ │ └── icons/ # Lucide SVG 图标集50 个 │ ├── config/ │ │ ├── schema.py # 配置结构定义 │ │ └── defaults.json # 默认动作 / 规则 / 事件链 │ └── diagnostics/ │ └── capture_workflow.py # 截图 → OCR → 提问 全流程 ├── installer/ # C# 编译的安装器.NET Framework ├── tests/ # 26 个测试文件 ├── docs/ # 设计方案 / 隐私设计 / 开发与发布规范 └── _doc/prd/ # 6 份需求文档PRD三、Win32 接入层为什么不用 pywin32这是整个项目最有立场的一个技术决策。3.1 依赖问题requirements.txt里刻意没有pywin32和pynput# 刻意不列 pywin32 / pynputWin32 接入层用纯标准库 ctypes 实现# 见 easy_tools/win32/理由见 easy_tools/win32/__init__.py。# 少两个原生依赖就少两类换 Python 版本装不上的问题。实际收益原生扩展.pyd是 Python 小版本绑定的。用户在 Python 3.11 / 3.12 / 3.13 之间切换时pywin32 常常要重新编译或找不到匹配 wheel。ctypes 是标准库跟着解释器走不存在这个问题。整个 Win32 层鼠标钩子、SendInput、窗口枚举、剪贴板、GDI 抓图不到 1400 行全是结构体声明 函数指针。3.2 全局鼠标钩子核心是WH_MOUSE_LL低级钩子SetWindowsHookExW(WH_MOUSE_LL,hook_proc,hmod,0)hook_proc必须是可持久化的函数指针不能是临时 lambda——否则回调触发时已被回收直接崩。项目里用一个模块级函数 CFUNCTYPE持有引用HOOKPROCWINFUNCTYPE(c_longlong,c_int,UINT,WPARAM,LPARAM)_procHOOKPROC(_mouse_proc)# 关键模块级持有防止 GC_hhkuser32.SetWindowsHookExW(WH_MOUSE_LL,_proc,hmod,0)3.3 短按 vs 拖动的判定中键在浏览器/编辑器里是滚动直接劫持会毁掉用户习惯。判定逻辑按下 → 记录 (x, y, timestamp) ├── 位移 drag_threshold_px 且 释放时间 - 按下时间 tap_timeout │ → 判定为「呼出面板」并按配置决定是否 swallow吞掉这次点击 └── 否则 → 完全交给系统触发原生中键滚动drag_threshold_px和消费模式native_passthrough/swallow_tap都在配置中心可配。这个阈值是 v0.3.0 统一到所有触发键的——中键、X1、X2 走同一套判定逻辑。对应的回归测试在tests/test_mouse_hook.py钩子结构体声明有tests/test_win32_decls.py守着。3.4 文本投递的两种实现粘贴模式兼容性最好backupget_clipboard()# 1. 备份set_clipboard(text)# 2. 写入send_keys(CtrlV)# 3. 模拟粘贴QTimer.singleShot(delay,lambda:set_clipboard(backup))# 4. 延迟恢复第 4 步的延迟恢复是关键——立刻恢复会让目标应用还没读完就丢数据。逐字键入模式SendInput逐字符构造KEYEVENTF_UNICODE天然支持中英文数字混排和换行\r→ 回车。速度可配基础间隔 随机抖动按Esc随时中断。四、上下文识别两级融合4.1 L1 — 进程名 标题正则零成本fgget_foreground_window()procget_process_name(fg)# → WINWORD.EXEtitleget_window_title(fg)# → 报销单2026.docx - Word直接读耗时在微秒级。规则匹配支持进程名、标题正则、OCR 关键词三个条件。4.2 L2 — 本地 OCR带防抖和缓存# 窗口切换后防抖 ~300ms 再触发识别# 结果按 (hwnd, size, mtime) 缓存# 后台线程执行面板弹出不等它关键设计面板弹出不等 OCR。先按 L1 规则把面板渲染出来L2 结果到达后再动态补充分组。所以识别再慢也不影响弹窗手感。4.3 融合策略{processes:[WINWORD.EXE,wps.exe],match:{titleRegex:\\.docx?$,ocrKeywords:[插入,页面布局]},action_ids:[word.cleanup,word.trim,word.md_table]}匹配到的动作组会排在公共动作之前——用户在 Excel 里90% 的意图是处理表格。五、事件链引擎5.1 数据结构{id:chain_trim_transpose,name:整理选中材料,trigger:{type:manual},steps:[{type:action,id:clipboard.copy,output:sel},{type:action,id:text.drop_blank_lines,input:{sel}},{type:condition,expr:has_table},{type:action,id:table.transpose,input:{step2.output}},{type:action,id:input.paste,input:{step4.output}}]}步骤类型action/condition/loop/wait/python/clipboard处理器。变量系统{clipboard}、{selection}、{ocr_text}、{stepN.output}、{var.name}在步骤间传递。5.2 处理器复用设计上的一个巧思剪贴板处理器和事件链步骤是同一套东西。40 个处理器大小写转换、日期格式化、千分位、去重、排序、表格转 CSV、Markdown 表格互转……既能在面板里当按钮点也能直接拖进链里当步骤。# easy_tools/core/actions/processors.py# 注册的处理器同时是链的可执行节点case.upper/case.lower/case.title/case.camel/case.snake/case.kebab date.format/date.now/date.weekday number.thousands_sep/number.fixed_decimals/number.sum/number.strip_sep data.dedupe/data.sort_lines/data.padding/data.normalize_width/data.add_numbers table.transpose/table.csv/...5.3 Python 节点的隔离设计Python 节点在独立进程中运行有超时保护卡死不会拖住主界面。但必须说清楚这不是沙箱。获授权的脚本拥有当前 Windows 账户的全部权限——能读写文件、访问网络、自己调PrintWindow截屏甚至能绕过 Easy-Tools 对自身窗口设置的SetWindowDisplayAffinity排除。所以 UI 上必须手动勾选信任运行授权 ☐ 我信任此脚本并接受它使用当前 Windows 账户权限代码里对应的也是老老实实的双重检查UI 状态 运行前再校验不是装饰。5.4 v0.3.0 修的事件链 Bug这个 bug 挺典型值得单独说链卡片根本没接点击/选中事件所以「编辑选中 / 删除选中 / 立即运行选中」永远提示请先选中编辑对话框关闭时引用了QDialogButtonBox.Accepted——Qt 里根本没有这个常量只有Accepted直接抛AttributeError崩溃修法卡片单击选中高亮、双击直接进编辑常量改正。教训PySide6 里枚举是Qt.ItemDataRole.UserRole这种形式不是QDialogButtonBox.Accepted。这类错误 IDE 不会提示只能靠 review 和测试兜住。六、隐私设计写成硬约束不是口头承诺docs/privacy-design.md里明确写了适用规则且规定新增功能不得绕过6.1 窗口捕获排除开启隐私模式后所有顶层窗口继承CapturePrivacyFilter的捕获排除状态基于SetWindowDisplayAffinity(WDA_EXCLUDEFROMCAPTURE)。关键约束也是大多数工具做不到的Easy-Tools 发起截图、OCR、Canvas 等内部流程时不得临时移除自身窗口的捕获排除属性。很多工具的隐私模式是靠截图时临时取消排除实现的然后忘记加回去——结果就是自己截到了自己的面板。这里明确禁止了这种做法。新建对话框必须由 Qt 应用持有让全局过滤器能观察到显示事件不允许创建绕过过滤器的独立原生窗口。6.2 能力边界写清楚不吹牛的部分只覆盖受支持的 Windows/DWM 捕获 API不能防止直接读进程内存、辅助功能接口读取、外部摄像设备拍摄Easy-Tools 自己发起的截图仍可处理目标应用内容6.3 数据流数据默认去向OCR 文本、选区、剪贴板、知识库内容仅本机云端模型调用用户显式选择后问题/OCR 内容/检索片段离开本机界面明确提示CC Switch 凭据仅调用时只读不写入配置 / 常规日志 / 错误弹窗调试日志不记录完整问题、OCR 内容、剪贴板内容、文件内容、令牌七、UI 工程PySide6 踩过的坑7.1 GUI 主线程绝不阻塞这是ET-PERF-001的核心验收点起因是用户反馈偶发无响应。排查后定的规矩题库导入、表头读取、配置确认 → 移到有上限的后台任务GUI 列表刷新不重新读 Excel只读内存中的已解析数据OCR 和内置模型限制 CPU 并发给界面留资源模型启动/停止不在 GUI 线程等长锁重复提问/截图任务不无限积压显示处理中状态7.2 托盘启动的初始化顺序PRD 里记了一条很具体的约束启动时托盘通知槽必须先定义后连接回调再次检查隐私通知开关最终 EXE 的正常启动路径能进入托盘就绪阶段不出现未定义回调异常。这种 bug 只在特定时序下出现测试很难覆盖所以直接写进验收点。7.3 图标系统v0.2.1 把全站 emoji 换成了 Lucide SVG50 个矢量图标ISC 许可免费可商用动作面板 27 个默认动作、面板顶部设置/关闭按钮、复选框勾选样式全部替换配置中心 14 个页面各配语义对应图标高 DPI 屏按 2 倍像素渲染QIconQPixmap手动setDevicePixelRatio图标加载做了优雅降级——图标文件缺失时条目照常创建只是没图标不阻塞启动markself._MARKS.get(title)ifmarkandicons.has_icon(mark):item.setIcon(icons.icon(mark,#475569,18))7.4 显示层和存储层分离easy_tools/ui/labels.py是个很典型的设计。原始配置中心直接把内部代码甩给用户类型 chain.run 参数 {snippet_id: snip_a, mode: }这个模块专门做「翻译」defkind_label(kind):# chain.run → 运行事件链defprocessor_label(pid):# text.strip → 去除首尾空白defmode_label(mode):# type → 逐字键入边界写得很清楚只做翻译不改存储格式。用户看到的中文绝不会被写回配置。找不到映射时如实显示原文绝不假装认识——宁可漏翻不能错翻。八、题库检索的一个设计细节知识库导入 Excel 后需要配置列映射题目、题型、正确答案、答案内容、选项列。其中「仅显示答案内容」这个选项值得单独说。很多题库的选项顺序是随机的同一道题A/B/C/D 的内容每次都不一样。如果按B去匹配答案命中率会随机下降。勾上这个选项后工具直接显示正确答案对应的那段内容即答案内容列的值而不是字母。选项打乱也不影响。这个功能明显是实际用过题库才加得出来的——它是业务知识的产物不是技术推导的产物。性能上做了预计算题干规范化 词项索引在导入时算好配置或文件变化后重建索引移除题库时释放缓存。避免每次查询都遍历 Excel。九、安装器.NET FrameworkC# 单文件installer/下是一个用 C# 写的安装器csc.exe编译不走 NSIS/InnoSetup。它解决的问题比拷贝文件多升级识别同时查注册表记录 常规旧版本目录默认目录、临时目录精准结束进程只结束该安装目录下的进程不误杀同名程序配置保留覆盖升级时保留用户配置APPDATA和非程序文件旧文件清理对比安装清单清理新版本已移除的旧文件卸载安全按清单删除 校验绝对路径边界保留清单外文件和用户配置注册表和快捷方式只清理归属当前安装目录的项不使用递归 CMD/PowerShell不保留后台重启循环清理重试有上限打包产物build.pyPyInstaller目录模式~451MB 含模型build_installer.py单文件安装器~408MB。模型权重models/*.gguf因超过 GitHub 单文件限制不入库。十、开发流程PRD 驱动_doc/prd/下 6 份需求文档每份都有编号和验收点编号主题ET-UX-001动作面板与文本输入ET-EVENT-001事件链构建器ET-QA-001截图题库问答ET-PRIV-001窗口捕获隐私ET-PERF-001卡顿排查与稳定性ET-UPDATE-001灰度发布docs/开发与发布规范.md定义了完整的版本发布和 GitHub Releases DownloadCenter 灰度流程。有意思的一条约定PRD 与记录不由 AI 标记为完成。工具能写代码能改文档但需求是否真的验收通过得人来判。十一、快速上手开发者git clone https://github.com/FortC/Easy-Tools.git cd Easy-Tools python-m pip install-r requirements.txt# 可选OCR 后端不装照常运行配置中心会显示不可用 原因python-m pip install rapidocr_onnxruntime python main.py打包Windows需 PyInstaller .NET Framework C# 编译器 模型文件python build.py# 目录程序python build_installer.py# 单文件安装器跑测试python-m pytest tests/-q十二、小结Easy-Tools 不是功能最多的桌面效率工具但有几个实现值得抄纯 ctypes 消灭原生依赖问题——1400 行换来跨 Python 版本零摩擦短按/拖动判定——劫持全局按键时保住原生行为的正确做法两级上下文识别 面板不等 OCR——识别精度和弹窗速度可以兼得处理器与链步骤同构——一份实现两个入口Python 节点进程隔离 显式信任勾选——诚实地说明边界而不是假装沙箱隐私写成硬约束文档——禁止截图时临时取消自身排除这类偷懒实现如果你也在写 Windows 桌面工具这几个坑可以避开。联系方式lamthebestfoxmail.com 欢迎提 Issue 和 PR

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑