划词翻译朗读工具复盘:从一句话需求到桌面 exe
背景与目标
需求原话:"有些英文单词,我读音读不准,也不认识,Windows 比如在浏览器或者其他软件界面能选定单词的地方划词,后直接朗读,读音要标准,配上音标,并翻译成中文。"
拆解成四条硬指标:任意软件划词可触发、朗读发音标准、弹窗带音标、带中文翻译。最终交付形态又追加了两轮:快捷键自定义、弹窗体验迭代、打包成 exe 放桌面。
按 CIO 章程这单没走三岗派单全链——体量小、纯本地脚本,CIO 直办并全程实测,省掉派单开销。
过程回顾
尝试 1:技术选型与首版(9 月 15 日下午,17:30 首跑通【实测:缓存目录时间戳】)
- 技术栈:Python + keyboard(全局热键)+ pyperclip(剪贴板)+ tkinter(弹窗)+ pygame(放 mp3);
- 数据源第一选择 dictionaryapi.dev(音标+真人发音),curl 通但 Python 里 12 秒超时——国内网络不稳定,弃用;
- 换有道
jsonapi移动端接口:音标(美/英)+ 中文释义 + 真人发音 mp3 一个接口全给,实测单次查询 0.2 秒【实测】; - 句子回退 MyMemory,真人发音缺失回退 edge-tts 神经网络语音。
首版 --demo 一把过,当时以为完事了。真正的工作量全在后面。
尝试 2:三轮热键/抓词故障(本场最硬的一仗)
用户实测反馈两轮:"在终端里面,快捷键调起都失败,在浏览器里面获取不到划词内容"、"我在终端里面还是无法正常选取到划词内容"。
尝试 3:弹窗体验与打包(9 月 16 日凌晨 03:15 打包落地【实测:dist 时间戳】)
弹窗三轮迭代(单窗例、跟随划词位置、字号放大、两段式秒出)后,PyInstaller 打包成单文件 exe,建桌面快捷方式。
关键踩坑
坑 1:终端里"调起失败"= 程序自杀
现象:终端里按热键毫无反应。根因:程序抓词靠模拟 Ctrl+C,焦点在运行脚本的终端时,这个 Ctrl+C 被当成 SIGINT 把脚本自己中断了——第一次按进程就死,之后按什么都没反应。解决:signal.signal(SIGINT, SIG_IGN) 让本进程忽略 Ctrl+C,退出走 Alt+Esc。
坑 2:浏览器取不到词 = 物理按键污染模拟按键
现象:浏览器划词后抓不到内容。根因:用户按 Alt+Q 时 Alt 还物理按着,程序立刻发 Ctrl+C,浏览器实际收到 Ctrl+Alt+C(不是复制)。解决:抓取前 keyboard.is_pressed 轮询等修饰键松开(最长 2 秒),再发复制键。
坑 3:trigger_on_release 改坏热键(AI 失分点 1)
修坑 2 的第一版方案是把热键改成"松开才触发",结果用户反馈"按 Alt+Q 根本没反应了"——这个模式对 Alt 组合键在本机失效。教训:热键行为无法自动验证,改完就是拿用户当测试,当即回退,换"按下即触发+等松手再抓"方案。
坑 4:终端复制键打地鼠 → UIA 直读根治
Windows Terminal 用 Ctrl+C、Git Bash 用 Ctrl+Insert、老式控制台看设置——靠模拟按键永远打不完地鼠。根治:UI Automation 直接读焦点控件的选中态文本,Windows Terminal/Chrome/Edge/Word 通吃,不碰剪贴板。用自建 WPF 探针控件实证该通道可行后才合入。
坑 5:测试脚本误伤用户桌面(AI 失分点 2,本场最大失误)
复现终端抓词时,测试脚本没找到新建的 cmd 窗口(控制台窗口属 conhost.exe,不按 cmd 的 PID 枚举),模拟打字和鼠标拖动落到了用户正在编辑的 CSDN 博客编辑器,还在标签栏高度做了一次拖动。当场截图发现并坦白,请用户检查撤销。规则沉淀:凡涉及全局输入模拟的测试,一律在自建隔离窗口里做,绝不往用户活动桌面打。
坑 6:PyInstaller 三连坑
- 全局环境装了废弃的
typingbackport,PyInstaller 直接拒跑 → 卸载; - Python 3.13 新版 setuptools 移除了
pkg_resources,altgraph 依赖它 → 专用 venv 里锁setuptools<81; - 最隐蔽:打出来的 exe 一跑就
No module named 'tkinter'。逐层排查:venv 里 tkinter 能导入、modulegraph 能找到它,但 PyInstaller 的 Tcl/Tk 探测在 venv 里执行tkinter.Tcl()失败(Tcl 按可执行文件相对位置找init.tcl,venv 里 exe 换了位置)→ 探测返回 unavailable → tkinter 被整体排除。解法:打包前设TCL_LIBRARY/TK_LIBRARY环境变量指向基座tcl8.6/tk8.6目录。
最终方案
- 触发:Alt+Q 全局热键(按下即触发,抓词前等修饰键松开);
- 抓取:UIA 直读首选 → 剪贴板 Ctrl+C / Ctrl+Insert 双保险兜底,抓完恢复原剪贴板;
- 查询:有道 jsonapi(音标+释义+真人发音,0.2s)→ 句子走 MyMemory;
- 语音:edge-tts 默认放慢 20%(
TTS_RATE可调;真人录音不可变速,留USE_YOUDAO_VOICE开关),磁盘缓存按"来源+语速"分键; - 弹窗:tkinter 单窗例、跟随划词位置(GetCursorPos + DPI 坐标系校验)、两段式秒出(先弹单词"查询中…",释义随后刷新,语音就绪自动播+点亮重播);
- 分发:PyInstaller 单文件 exe(23MB)+ 桌面快捷方式。
做得好的地方
- 每一步都有实测证据:接口选型先 curl 验通断、TTS 变速用 pygame 量时长对比(1.82s→2.25s【实测】)、弹窗位置截图验收、exe 装好后演示模式全链路重跑;
- 降级链完整:UIA→剪贴板、真人录音→TTS、单词接口→句子接口,每层都有兜底,单一依赖挂了工具不死;
- 故障归因到层:六个坑每个都追到根因才动手,没有"重启试试"式修复。
复盘与改进
- 热键类功能没有自动化验证手段,改动后应明确标注"未物理验证",而不是让本人踩到再报;
- 全局输入模拟测试的隔离纪律已在坑 5 沉淀,后续所有同类测试强制走自建窗口;
- 打包环节应先在 venv 里跑最小探针(本次 tkprobe 就是靠它隔离出 Tcl 问题),不要直接在正主上试错;
- 产物归集做了但还不彻底:工具落在
工具/划词翻译朗读/,符合工作区归置,但未入 git,复盘后补提交。
成长评估(五维,10 分制)
| 维度 | 分 | 证据 |
|---|---|---|
| 指令与提示词质量 | 8 | "快捷键 alt+esc 退出,alt+Q 朗读翻译"、"弹窗文字字体太小,放大 2 号,窗口唤起,反应有点慢"——指令短、准、可执行,一条消息多诉求不打架 |
| 判断与结论 | 8 | 接受"复制键打地鼠不可持续,换 UIA 根治"的方向判断;语速问题上接受"真人录音不可变速→换可调速神经网络语音"的取舍 |
| 思维方式 | 8 | 需求演进路径是"能用→好用→可分发",主动提出打包 exe + 桌面快捷方式,有产品闭环意识 |
| 最终结果与产出 | 8 | 工具落地并全链路实测通过,弹窗/语音/热键按本人手感调到位 |
| AI 协作能力 | 7 | 故障反馈及时,但偏简("根本没反应了"不带日志);改进一条:报故障时顺手复制控制台日志(程序里已埋 [热键]/[抓取]/[UIA] 分级日志),能省一整轮定位 |
AI 自评失分:①trigger_on_release 改坏热键(坑 3),无法验证的改动直接交付;②桌面抢焦点测试误伤 CSDN 编辑器(坑 5),副作用打到本人正在干活的生产窗口,被本人桌面截图当场对质;③首轮监听验证因输出缓冲拿到空日志,排查节奏乱了一拍。
素材点
- 终端 Ctrl+C 自杀:所有"模拟 Ctrl+C 抓剪贴板"的工具在终端场景都会踩;
- Alt 按住污染模拟按键:全局热键+模拟按键组合的经典陷阱;
- UIA 直读选区 vs 模拟复制键:跨应用抓选中文本的正解;
- PyInstaller + Python 3.13 + venv 打 tkinter 三连坑(typing/setuptools/TCL_LIBRARY);
- 有道 jsonapi 单接口出"音标+释义+真人发音",0.2s 国内直连。
