Pywinauto 简介
Pywinauto 是一个用于自动化 Windows 图形用户界面(GUI)的 Python 库。它允许你通过编程方式控制 Windows 应用程序,模拟用户操作(如点击按钮、输入文本、选择菜单等)。简单来说,它就是让 Python 成为你的'机器人助手',代替鼠标和键盘去操作电脑上的软件。
核心特点
-
跨 GUI 框架支持
- Win32 API (backend='win32'):适用于 MFC、VB6、WinForms 等传统界面。
- MS UI Automation (backend='uia'):适用于 WPF、Qt、Modern UI(UWP)等较新的界面。
-
简单易用的 API
- 支持通过窗口标题、类名、控件类型等属性定位元素。
- 提供类似自然语言的链式调用,例如
window.Edit.type_keys('hello, world')。
-
辅助工具集成
- 推荐使用
inspect.exe或UISpy.exe辅助识别控件属性,方便调试。
- 推荐使用
环境搭建与基础操作
安装依赖
通过 pip 安装即可,无需复杂配置:
pip install pywinauto
安装完成后,建议运行 pip list 确认版本信息。
启动与连接应用
启动新程序
使用 Application.start() 方法可以启动指定的应用程序。路径可以是相对路径,也可以是绝对路径。
from pywinauto.application import Application
# 启动记事本
app = Application(backend='uia').start('notepad.exe')
连接已运行的程序
如果程序已经打开,可以通过进程 ID 或窗口句柄进行连接。
# 通过进程 ID 连接
app = Application(backend='uia').connect(process=12345)
# 通过窗口句柄连接
app = Application(backend='uia').connect(handle=66666)
*注意:实际运行时进程 ID 会变化,调试时请替换为当前真实值。
窗口定位与控制
定位窗口
我们可以结合 print_control_identifiers() 方法打印控件树,协助定位目标窗口。
# 精确匹配标题
win = app.window(title='Hello, Pywinauto! - Notepad')
# 正则匹配
win = app.window(title_re='.*Notepad')
# 模糊匹配(慎用,特殊符号可能导致报错)
win = app.window(best_match='Hello, Pywinauto! - Notepad')
# 按类名匹配
win = app.window(class_name='Notepad')
常用窗口操作
close(): 关闭窗口maximize(): 最大化minimize(): 最小化restore(): 恢复大小wait('visible'): 等待窗口可见
# 连接并操作示例
app = Application(backend='uia').connect(process=22860)
win = app.window(title_re='.*Notepad')
win.wait('visible')
win.maximize()
time.sleep(3)
assert win.is_maximized()
win.minimize()
time.sleep(3)
assert win.is_minimized()
win.restore()
assert win.is_normal()
win.close()
控件定位与交互
查找控件
定位控件通常有三种方式:打印控件树、使用 UISpy 工具、或者根据已知属性直接查找。
常见的控件类型包括对话框(Dialog)、编辑栏(Edit)、按钮(Button)、列表框(ListBox)、菜单(Menu)等。以下是部分常见控件对照表:
| 分类 | 控件名称 | 说明 |
|---|---|---|
| 窗口与对话框 | Dialog | 用于交互,如警告弹窗 |
| 输入控件 | Edit | 文本输入框 |
| 输入控件 | Button | 触发操作的按钮 |
| 输入控件 | ComboBox | 下拉组合框 |
| 导航控件 | TreeView | 树状视图 |
| 显示控件 | Static | 静态文本 |
打印控件信息后,可以使用以下语法定位:
# 通过标题定位
save_btn = win['保存']
# 通过属性定位
save_btn = win.child_window(auto_id='SaveBtn', control_type='Button')
*注意:如果定位到多个相同控件,可使用 found_index 参数指定索引(从 0 开始)。
等待机制
自动化脚本中,等待至关重要。Pywinauto 提供了多种等待状态:
exists: 窗口句柄有效visible: 窗口可见enabled: 窗口未被禁用ready: 可见且启用active: 处于活动状态
# 等待按钮可用
mem_btn.wait('enabled')
# 等待直到特定条件满足
def get_status():
return win.is_visible()
wait_until(3, 0.5, get_status, True)
鼠标与键盘操作
当控件级操作无法满足需求时,可以使用 mouse 和 keyboard 模块模拟底层事件。
鼠标操作:
from pywinauto.mouse import click, move
# 点击指定坐标
mid_point = key.rectangle().mid_point()
click(coords=(mid_point.x, mid_point.y))
键盘操作:
from pywinauto.keyboard import send_keys
# 发送焦点窗口
send_keys('1234567')
# 在指定控件输入
win.type_keys(keys='好好学习,天天向上', pause=1)
# 特殊键码
win.type_keys(keys='abcd{ENTER}efg')
win.type_keys(keys='{TAB 10}')
常用虚拟键码参考:
{ENTER}: 回车{TAB}: Tab 键{BACKSPACE}: 退格{ESC}: 退出{UP}/{DOWN}/{LEFT}/{RIGHT}: 方向键{SHIFT}: +{CTRL}: ^{ALT}: %
菜单与列表操作
菜单操作:
# 获取所有菜单项
menu_bar.items()
# 按路径查找
save = menu_bar.item_by_path(path="File -> Save", exact=True)
save.click_input()
# 直接选择菜单
win.menu_select(path='File -> Open Recent')
列表操作:
list_ctrl = win.child_window(title='项目视图', control_type='List')
# 获取所有项
items = list_ctrl.get_items()
# 双击某一项
list_ctrl.get_item(row=0).double_click_input()
实战案例:微信自动发消息
下面以微信文件传输助手为例,演示一个完整的自动化场景。主要思路是连接进程、记录消息条数、发送消息、验证结果。
实现逻辑
- 连接微信进程,定位聊天窗口。
- 发送前记录当前消息列表数量和内容。
- 构造带时间戳的唯一消息内容并发送。
- 发送后再次检查消息数量和最新内容是否匹配。
代码实现
"""
微信自动发送消息示例
"""
from datetime import datetime
from pywinauto.application import Application
# 1. 连接微信进程 (需替换为实际 PID)
app = Application(backend='uia').connect(process=2832)
# 2. 定位窗口
win = app.window(title='文件传输助手', control_type='Window')
win.wait('visible')
# 3. 发送消息准备
message_list_before = win.child_window(title='消息', control_type='List')
message_count_before = message_list_before.item_count()
# 生成唯一消息内容 (使用时间戳避免重复)
message = 'hello, world!' + ' - ' + str(datetime.now())
# 定位输入框并输入
message_edit = win.child_window(title='输入', control_type='Edit')
message_edit.click_input()
message_edit.type_keys(keys=message, with_spaces=True)
# 点击发送按钮
send_btn = win.child_window(title='发送 (S)', control_type='Button')
send_btn.click_input()
# 4. 校验结果
message_list_after = win.child_window(title='消息', control_type='List')
message_count_after = message_list_after.item_count()
# 校验数量 (可能增加 1 条消息或 1 条消息 +1 条时间)
assert message_count_after == message_count_before + 1 or \
message_count_after == message_count_before + 2
# 校验内容
latest_msg = message_list_after.get_item(row=message_count_after - 1).window_text()
assert latest_msg == message
注意事项
- 消息唯一性:如果发送内容完全一致,很难验证是否成功。建议拼接时间戳保证每条消息唯一。
- 数量校验:微信发送成功后,列表可能增加一条消息,也可能增加两条(消息本身 + 时间标记),断言时需兼容这两种情况。
- PID 变化:示例中的进程 ID 仅为演示,实际运行前请使用任务管理器获取微信的真实 PID。
- 后端选择:微信新版多采用 Uia 后端,若遇到问题可尝试切换
backend='win32'测试兼容性。


