App 自动化做熟之后回头做 PC 端,最大的感受是:Windows 桌面比移动端更不标准——同一个"对话框",Qt、WPF、Win32、Electron 各长各的。pywinauto 是覆盖最广的选择,这篇记录控件树分析和实战中最重要的几手。
一、两种 backend,先分清楚
1 | from pywinauto import Application |
选型经验:
- 新框架(Qt5+、WPF)一律先试
uia; - 老系统工具类程序(控制面板、传统 MDI)用
win32更快; - Electron 应用:UIA 只能看到窗口级结构,内部 DOM 要走 CDP(
--remote-debugging-port),别硬刚控件树。
二、控件树分析:dump 先行,拒绝盲写选择器
pywinauto 最大的价值是把控件树变成可遍历的 Python 对象:
1 | # 1) 全树 dump 到文件,先看结构再写脚本 |
实战约定:控件定位写选择器前,先 dump 出 auto_id 清单和开发对齐——桌面应用的 auto_id 往往藏在代码的 ObjectName/AutomationProperties.AutomationId 里,和 App 端的 resource-id 是同一个道理:有约定就永远有稳定定位。
三、虚拟列表:滚动加载的坑
长列表(万行级别)在 UIA 里是虚拟化的——只渲染可见区域,get_item(5000) 直接报"元素不在可视区"。解法:
1 | def find_row(grid, text, max_scrolls=200): |
要点:
- 滚动步长要自适应:一次滚太多会跳过目标(虚拟化渲染有缓冲),30~50 行是安全步长;
- 找不到先确认"到底了没有"再抛超时,区分"不存在"和"没滚到";
- 定位到行之后,先滚动到可见再点击,
click_input()要求元素真实可见。
四、多窗口/子窗口管理
桌面应用动辄主窗口 + 弹窗 + 子面板:
1 | # 弹窗出现后接管 |
with 上下文管理窗口生命周期是 pywinauto 最省心的设计:弹窗关掉、窗口销毁都自动处理,不会留下野引用。
五、稳定性:桌面版的三板斧
和 App 端一脉相承:
- 等待:
wait("visible ready")永远带上 timeout,禁止time.sleep; - 焦点:
click_input()是真实鼠标点击,受焦点/遮挡影响——自动化期间保证目标窗口在最前(restore()+set_focus()),被其他窗口遮挡时click_input会点错地方; - 失败现场:失败时
win32gui截屏 +print_control_identifiers导出当前树,两个产物一起归档,定位"控件树变了还是时序问题"一目了然。
六、小结
PC 端自动化的核心是先理解控件树,再写选择器:backend 选对、auto_id 有约定、虚拟列表会滚动、窗口生命周期用 with 管。做到这四点,pywinauto 的脚本稳定性和 App 端可以打平。