一句话需求到桌面 exe:我的划词翻译朗读工具,一下午跑通,修坑修到凌晨三点
9 月 15 日下午,我给自己下了个单
划完词按一下 Alt+Q,音标、中文翻译、标准发音直接弹到眼前:这个 Windows 小工具我从需求做到打包落地花了十个小时,九成时间耗在后面的坑里。
需求是我自己提的,原话照贴:
"有些英文单词,我读音读不准,也不认识,Windows 比如在浏览器或者其他软件界面能选定单词的地方划词,后直接朗读,读音要标准,配上音标,并翻译成中文。"
平时啃英文文档,碰到生词的动作是:划词、复制、切到翻译软件、粘贴、看完再切回来。一天几十次。我想要的是划完词按一下键,音标、中文、标准发音直接弹到眼前。
拆成四条硬指标:任意软件里划词可触发、发音标准、弹窗带音标、带中文翻译。
这单我没走团队派单全链。先交代一下:军师是我用 Kimi Code 扮演的总调度,凌驾我所有数字员工团队,负责派单、验收、扛"定性"——历史军师只有一颗人脑和有限情报,它背靠我全部项目数据和整支数字员工团队,24 小时不下班。这个工具体量小、纯本地脚本,军师直办并全程实测,省掉派单开销。
选型:第一个数据源 12 秒超时,直接枪毙
技术栈很快定了:Python + keyboard 管全局热键,pyperclip 管剪贴板,tkinter 弹窗,pygame 放 mp3。
数据源第一站是 dictionaryapi.dev,音标加真人发音,看着很全。curl 能通,Python 里却 12 秒超时——实测,国内网络不稳,弃用。
换有道的 jsonapi 移动端接口,一个接口把音标(美/英)、中文释义、真人发音 mp3 全给了,实测单次查询 0.2 秒。句子翻译回退 MyMemory,真人发音缺失回退 edge-tts,能调语速。
首版 --demo 一把过,17:30 跑通,这个时间是缓存目录时间戳记的。
我以为完事了。
真正的工作量全在后面。
三轮故障:它先把自己杀了,又被我的手指骗了
我实测后的反馈原话两条:
"在终端里面,快捷键调起都失败,在浏览器里面获取不到划词内容"
"我在终端里面还是无法正常选取到划词内容"
先说终端里的"调起失败"。追到底才发现是程序自杀:抓词靠模拟 Ctrl+C,焦点恰好停在运行脚本的终端时,这个 Ctrl+C 被终端当成中断信号,把脚本自己杀了。第一次按进程就死,之后按什么都没反应。解法一行:
signal.signal(signal.SIGINT, signal.SIG_IGN)
让本进程忽略 Ctrl+C,退出改走 Alt+Esc。
浏览器取不到词是另一个机关。我按 Alt+Q 的时候,Alt 还物理按着,程序立刻发 Ctrl+C,浏览器实际收到的是 Ctrl+Alt+C——根本不是复制。解法:抓取前轮询等修饰键松开,最长等 2 秒,再发复制键。
修这个坑的第一版方案,军师把热键改成了"松开才触发"。结果我按 Alt+Q 直接没反应了——这个模式对 Alt 组合键在我机器上整体失效。热键行为没法自动验证,改完就是拿用户当测试。当即回退,换成"按下即触发、等松手再抓"。
终端复制键是打地鼠:Windows Terminal 吃 Ctrl+C,Git Bash 吃 Ctrl+Insert,老式控制台看设置。模拟按键这条路永远打不完。根治方案是 UIA 直读焦点控件里选中的文本,Windows Terminal、Chrome、Edge、Word 通吃,不碰剪贴板。合入之前,先用一个自建 WPF 探针控件实证这条通道可行,才敢动正主。
六个坑总账,一张表说清
| 坑 | 根因 | 解法 |
|---|---|---|
| 终端调起失败 | 模拟 Ctrl+C 杀了宿主脚本 | 进程忽略 SIGINT |
| 浏览器抓不到词 | 物理 Alt 污染模拟按键 | 等修饰键松开再抓 |
| 热键改坏 | "松开才触发"对 Alt 失效 | 回退,按下即触发 |
| 终端复制键打地鼠 | 各终端复制键不统一 | UIA 直读选中文本 |
| 测试误伤桌面 | 没找到 conhost 窗口 | 隔离窗口测试 |
| 打包三连坑 | typing/setuptools/Tcl 探测 | 逐个环境排障 |
最大的失误:测试脚本打到了我自己的编辑器
这是全场我最绷不住的一幕。
复现终端抓词时,测试脚本要找一个新建的 cmd 窗口,没找到——控制台窗口挂在 conhost.exe 名下,不按 cmd 的进程枚举。于是模拟打字和鼠标拖动,落到了我正在编辑的博客编辑器里,还在标签栏高度拖了一次。
我是从桌面截图里当场发现的。军师当场坦白,让我检查撤销。
没有"已清理干净、无影响"这种话。误伤就是误伤。这条当场沉淀成规矩:凡涉及全局输入模拟的测试,一律在自建隔离窗口里做,绝不往活动桌面打。
打包:PyInstaller 三连坑,最隐蔽的是 tkinter 被静默扔掉
9 月 16 日凌晨,弹窗三轮迭代完(单窗例、跟随划词位置、字号放大两号、两段式秒出),开始打包。我调手感的原话:
"快捷键 alt+esc 退出,alt+Q 朗读翻译"
"弹窗文字字体太小,放大 2 号,窗口唤起,反应有点慢"
PyInstaller 连踩三坑。第一坑:全局环境装了废弃的 typing backport,PyInstaller 直接拒跑,卸载。第二坑:Python 3.13 的新版 setuptools 移除了 pkg_resources,altgraph 依赖它,在专用 venv 里锁 setuptools<81。
第三坑最阴:打出来的 exe 一跑就报 No module named 'tkinter'。可 venv 里 tkinter 明明能导入,modulegraph 也能找到它。逐层排查才定位:PyInstaller 的 Tcl/Tk 探测在 venv 里执行失败——Tcl 按可执行文件的相对位置找 init.tcl,venv 里 exe 换了位置,探测返回不可用,tkinter 被整体排除,一句话都不说。
解法:打包前把环境变量指回基座目录。
TCL_LIBRARY → 基座 tcl8.6 目录
TK_LIBRARY → 基座 tk8.6 目录
03:15,单文件 exe 落地,23MB,这个时间看 dist 目录时间戳。建桌面快捷方式,收工。
最终架构,每一层都有兜底
- 触发:Alt+Q 全局热键,按下即触发,抓词前等修饰键松开;
- 抓取:UIA 直读首选,剪贴板 Ctrl+C / Ctrl+Insert 双保险兜底,抓完恢复原剪贴板;
- 查询:有道 jsonapi 出音标、释义、真人发音,0.2 秒,句子走 MyMemory;
- 语音:edge-tts 默认放慢 20%,实测用 pygame 量时长,1.82 秒拉到 2.25 秒;真人录音不可变速,留了开关;
- 弹窗:tkinter 单窗例,跟随划词位置,两段式秒出——先弹单词显示"查询中…",释义随后刷新,语音就绪自动播。
降级链是完整的:UIA 挂了有剪贴板,真人发音缺了有 TTS,单词接口挂了有句子接口。单一依赖死了,工具不死。
这三条,你下次做同类工具直接抄
一,跨应用抓选中文本,正解是 UIA 直读,不是模拟复制键。模拟按键会被物理按着的修饰键污染,还会把终端里的宿主脚本自己杀掉,两个坑我都替你们踩过了。
二,凡涉及全局输入模拟的测试,一律在自建隔离窗口里做。我的博客编辑器就是反面教材。
三,打包排障先在 venv 里跑最小探针,别在正主上试错。这次就是靠一个 tkprobe 探针把 Tcl 问题隔离出来的,不然到现在还在猜。
一句话总结这十个小时:首版跑通只占一成工作量,剩下九成全在追根因——但没有一次是"重启试试"。
FAQ
问:这工具开源吗?能下载吗? 答:目前只在我自己桌面跑,代码和打包脚本还没整理到能公开的程度。思路全在正文里:keyboard + UIA + 有道 jsonapi + edge-tts + tkinter + PyInstaller,照着架构清单能自己攒一个。后续整理好会在公众号同步,评论区可以蹲。
问:为什么不直接用现成的划词翻译软件? 答:现成软件的发音和音标未必合我口味,更重要的是,这单的真实目的是练"一句话需求到本地产物"的产线,工具是顺手的战利品。
问:能翻译整段文档或者截图里的英文吗? 答:不能。它只处理"能划词选中"的文本,整段文档翻译和 OCR 划词都不在目前的能力范围内,我不夸大。
问:Python 3.13 打包 tkinter 程序,最该先查什么? 答:先查 setuptools 版本(锁 <81),再查全局有没有废弃的 typing 包,打包时设好 TCL_LIBRARY/TK_LIBRARY。顺序就是我踩坑的顺序。
我是野生码农,AI 实战派。自研知识库、数字员工、软考备考、量化交易四个系统,全程公开复盘。
本文所有内容均为本人 AI 实战的过程和结果,经 AI 整理后发布,无任何瞎编虚构内容。
这篇来自《AI员工团队》系列。下一篇,我把 Kimi Code 的 token 账单摊开实测,看看养这支团队到底烧了多少钱;每月月底还有月报,真实数据全公开。
关注我,看真的。
野生码农AI实战 · 全网同名
