《GUI 自动化详解(二):控件、鼠标键盘与菜单列表操作全指南》

一、等待

案例:没有添加等待导致引发的错误

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=24600)
win = app.window(title_re='.*Sublime Text.*')
#添加等待 
#win.wait('exists')
#最⼩化 
win.minimize()
print("is_minimized:",win.is_minimized())
win.maximize()
print("is_maximized:",win.is_maximized())
win.close()

把等待代码注视掉,程序运⾏报错:

GUI应⽤程序⾏为通常不稳定,脚本需要等待,直到出现新窗⼝或关闭/隐藏现有窗⼝。pywinauto可以隐式地(默认超时)灵活地等待对话框初始化,或者明确地使⽤专⽤⽅法/函数来帮助您使代码更容易和更可靠。

1.1 wait/wait_not()

  • wait(self, wait_for, timeout=None, retry_interval=None)
  • wait_not(self, wait_for_not, timeout=None, retry_interval=None)
参数说明:

wait_for :表⽰选择的窗⼝状态

  • exists :表⽰窗⼝是⼀个有效的句柄
  • visible :表⽰窗⼝不隐藏,可以看到
  • enable :表⽰窗⼝未被禁⽤,可操作
  • ready :表⽰窗⼝可⻅且已启⽤
  • active :表⽰窗⼝处于活动状态

timeout :表⽰超时

retry_interval :表⽰重试时间间隔,单位为秒s

wait_not 与 wait() 类似, wait 是等待处于某种状态,⽽ wait_not 是等待不处于某种状 态,这⾥以wait为例说明

使⽤⽰例:"exists" 和 "visible"
复制代码
from pywinauto.application import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
#检查窗⼝是有效的句柄 
win.wait('exists')
#检查窗⼝是否可⻅ 
win.wait('visible')
#检查窗⼝是否未被禁⽤ 
win.wait('enabled')
#检查窗⼝是否准备就绪 
win.wait('ready')

窗⼝被最⼩化之后,在桌⾯就不可⻅了,因此最⼩化时需要将等待状态改为 "exists" ,应⽤程序界 ⾯在桌⾯可⻅时等待状态可以设置为 "visible"

"enabled"

复制代码
#打开计算器 
# app = Application(backend="uia").start("calc.exe")
app = Application(backend="uia").connect(process=17892)
win = app.window(title="计算器")
win.wait("visible")
#启⽤的按钮 
enable_btn = win.child_window(title="记忆减法", auto_id="MemMinus", 
control_type="Button")
#未启⽤的按钮 
disabled_btn = win.child_window(title="清除所有记忆", 
auto_id="ClearMemoryButton", control_type="Button")
enable_btn.wait("enabled") #---代码执⾏通过 
disabled_btn.wait_not("enabled")#---代码执⾏通过 

若按钮置灰状态,说明按钮为 disabled 状态,当输⼊项不为空时,按钮⾼亮,此时为 enabled 状态

'ready'

复制代码
from pywinauto import Application
app = Application(backend="uia").connect(process=17892)
win = app.window(title="计算器")
win.wait("exists")
proc = win.child_window(title="打开导航", auto_id="TogglePaneButton", 
control_type="Button")
proc.wait("ready")#等待成功 
proc_chid = win.child_window(auto_id="PaneTitleTextBlock", control_type="Text")
proc_chid.wait("ready")#等待失败 

is_visible() ⽤于检查元素是否可⻅,除此之外, is_enabled() ⽤于检查元素是否启⽤

'active'

需要注意, 'active' 状态指的是窗⼝是否处于活动状态,需要先操作应⽤程序使得焦点设置在该窗 ⼝上或者配合 set_focus 来使⽤:

复制代码
from pywinauto import Application
sublime_app = Application(backend="uia").connect(process=9388)
sublime_win = sublime_app.window(title_re=".*Sublime Text.*")
app = Application(backend="uia").connect(process=17892)
win = app.window(title="计算器")
win.set_focus()
win.child_window(title="⼀", auto_id="num1Button", 
control_type="Button").click_input()
win.wait("active") #等待成功 
sublime_win.wait("active") #等待失败 

1.2 wait_until

等待满⾜某个条件 wait_until(timeout, retry_interval,func,value=True,op=operator.eq,*args, **kwargs)

部分参数说明:

  • Timeout :超时时间
  • retry_interval :重试时间
  • func :执⾏的函数
  • value :⽐较的值
示例1:
复制代码
i=0
def work():
   global i
   i += 1
   print("当前i的值为",i)
   return i
#等待work返回的结果为5,继续往下执⾏ 
wait_until(10,1,work,5)
print("等待通过")
⽰例2:
复制代码
def get_window():
    app = Application(backend='uia').connect(process=38544)
    win = app.window(title_re='.*Sublime Text.*')
    return win.is_visible()
def test_wait():
    #等待get_window⽅法返回结果为True 
    wait_until(10,2,get_window,True)
    print("等待通过")
test_wait()

pywinauto 提供了丰富的等待机制,可解决自动化过程中控件状态变化、窗口加载等问题,确保脚本能精准识别目标控件 / 窗口并在合适时机执行操作。本文聚焦实际应用中最常用的几种等待方法,帮助快速掌握核心技巧,保障自动化流程稳定可靠。pywinauto等待

二、控件的操作

2.1点击
1.click_input()
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=8732)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#点击最⼤化按钮 
win.child_window(title="最⼤化", control_type="Button").click_input()
2.right_click_input()

模拟⿏标右键单击操作

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=8732)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#右键窗⼝ 
win.right_click_input()
3.double_click_input()

模拟⿏标右键双击操作

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=8732)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
#双击菜单栏------达到窗⼝最⼤化⽬的 
menu.double_click_input()
2.2⽂本
1.texts()

⽤于获取窗⼝或控件中的所有⽂本内容,返回⼀个列表,其中每个元素是⼀个字符串,表⽰窗⼝或控 件中的某个⽂本⽚段

示例1:获取窗⼝的标题

复制代码
# app = Application(backend='uia').start("D:\software\Sublime Text 
3\sublime_text.exe")
app = Application(backend='uia').connect(process=2392)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
print(win.texts())

⽰例2:获取控件的标题

复制代码
# app = Application(backend='uia').start("D:\software\Sublime Text 
3\sublime_text.exe")
app = Application(backend='uia').connect(process=2392)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
file = win.child_window(title="File", control_type="MenuItem")
print(file.texts())
2.window_text()
⽤于获取窗⼝或控件的主⽂本内容,通常是指窗⼝的标题或主要显⽰的⽂本。它返回⼀个字符串。 示例1:
复制代码
# app = Application(backend='uia').start("D:\software\Sublime Text 
3\sublime_text.exe")
app = Application(backend='uia').connect(process=2392)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
print(win.window_text())
示例2:获取控件的标题
复制代码
# app = Application(backend='uia').start("D:\software\Sublime Text 
3\sublime_text.exe")
app = Application(backend='uia').connect(process=2392)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
file = win.child_window(title="File", control_type="MenuItem")
print(file.window_text())

三、⿏标操作

使用 pywinauto 做自动化测试时,常通过控件点击方法实现交互,这类方法支持直接点击控件,也可通过coords参数指定坐标点击。比如用win.double_click_input(coords=(1043, 130))点击 Sublime Text 窗口最大化按钮,语法虽无错误,但这种坐标点击方式在需要更高精度、更灵活的鼠标交互场景中,可能无法满足需求。

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#操作Sublime Text 窗⼝的最⼤化按钮--获取按钮的坐标 
win.double_click_input(coords=(1043,130))

为了解决这⼀问题,pywinauto提供了⼀个独⽴的mouse 模块,专⻔⽤于模拟真实⽤⼾的⿏标事 件。这个模块的优势在于,它完全独⽴于控件操作,能够更贴近真实用户的⾏为模式。通过mouse 模块,我们可以直接在屏幕上指定坐标进⾏点击、双击、拖动等操作,⽽⽆需依赖控件的层次结构。

示例: click() ⽅法操作SublimeText窗⼝的最⼤化按钮
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#通过⿏标点击 
mouse.click(coords=(1043,130))
⽰例:拖动滚动条

rectangle() :获取元素对应坐标,返回矩形尺⼨,具有top,left,right,bottom属性

mid_point() :获取元素中间位置坐标,返回类型为元组,元组中两个整数分别是X、Y轴的值

复制代码
from pywinauto import Application
# app = Application(backend='uia').start("mspaint.exe")
app = Application(backend='uia').connect(process=28372)
win = app.window(title="⽆标题 - 画图")
win.wait('visible')
#定位滚动条 
right_ScrollBar = win.child_window(title="垂直滚动条", 
auto_id="NonClientVerticalScrollBar", control_type="ScrollBar")
right_ScrollBar.wait('visible')
#获取滚动条中间位置 
mid = right_ScrollBar.rectangle().mid_point()
#从中间位置下拉 
mouse.scroll(coords=(mid.x,mid.y),wheel_dist=500)

mouse 模块常⻅操作:

⾃动化测试场景示例:抖⾳点赞
复制代码
# app = Application(backend="uia").start("C:\Program Files 
(x86)\ByteDance\douyin\douyin.exe")
app = Application(backend="uia").connect(process=14488)
win = app['抖⾳']
win.wait("visible")
#双击点赞 
for i in range(0,3):
 mid = win.rectangle().mid_point()
 mouse.double_click(coords=(mid.x,mid.y))
 time.sleep(1)
 #滑到下⼀个视频 
 mouse.scroll(coords=(mid.x, mid.y), wheel_dist=-500)

四、键盘操作

自动化测试中,pywinauto 的keyboard模块提供了强大的键盘操作能力,其中keyboard.send_keys()可直接向当前获焦窗口发送按键序列。但实际自动化场景中,我们通常需要针对文本框、按钮等特定控件精准输入,而非依赖窗口的焦点状态。

复制代码
from pywinauto.keyboard import send_keys
send_keys("1234567")

为满足精准输入需求,pywinauto 对键盘输入功能进一步封装,推出type_keys方法。该方法是 Edit、Button 等控件对象的专属方法,可直接作用于指定控件,确保输入精准,能避免焦点切换引发的输入错误,适配复杂界面的自动化操作。

语法:
复制代码
type_keys(
 keys, # 要输⼊的键序列,可以是普通字符、特殊键或组合键 
 pause = None, #每次按键后的延迟时间(秒) 
 with_spaces = False, # 如果为 True,则会在输⼊的字符串中保留空格。 
 with_newlines = False, #如果为 True,则会在输⼊的字符串中保留换⾏符。  
 ...
)

向当前焦点窗⼝⾃动键⼊键或发送⽂本。

4.1输⼊⽂本

直接输⼊⽂本(⽀持Unicode字符):

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#输⼊⽂本内容 
win.123("------type_keys------")
#保留换⾏符 
win.type_keys("------type_keys------\n",with_newlines=True)
#保留空格 
win.type_keys(" -----type keys----",with_spaces=True)
#延迟输⼊,避免输⼊过快导致内容不完整 
win.type_keys("⼀⼆三四五六七",with_spaces=True)
4.2按键

使⽤{VK_CODE} 格式表⽰虚拟键码,例如:

示例代码:
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#发送⽂本和回⻋ 
win.type_keys("Hello World{ENTER}",with_spaces=True)
win.type_keys("Hello bit",with_spaces=True)
指定重复次数 可以为特殊键指定重复计数。

{ENTER 2} 表⽰按两次Enter键

复制代码
#发送⽂本和回⻋ 
win.type_keys("Hello World{ENTER 2}",with_spaces=True)
win.type_keys("Hello bit",with_spaces=True)
转义特殊字符

使⽤{} 包裹特殊字符(如{+} ,{%} ,{^} )以避免被识别为修饰符

复制代码
win.type_keys("1+2=3") # 错误:`+` 会被识别为 Shift 
win.type_keys("1{+}2=3") # 正确 
4.3⾃动化测试场景⽰例:微信发消息

1)⽤例设计 GUI⾃动化⽤例的设计主要基于UI(界⾯)测试和界⾯展⽰的功能来进⾏⽤例的设计。

2)代码实现

以⽤例"验证用户输⼊⽂本后点击发送按钮,消息是否能成功发送并显⽰在聊天窗⼝中"为例

复制代码
# app = Application(backend="uia").start("D:\software\WeChat\WeChat.exe")
app = Application(backend="uia").connect(process=9768)
win = app.window(title="⽂件传输助⼿",handle=921080)
win.wait("visible")
#找到输⼊控件 
edit = win.child_window(title="输⼊", control_type="Edit")
edit.wait("ready")
#点击消息框 
edit.click_input()
#输⼊⽂本信息 
edit.type_keys("⼩⽐特666")
#点击发送 
win.child_window(title="发送(S)", control_type="Button").click_input()
#检查发送结果 
win.child_window(title="⼩⽐特666",control_type="ListItem").texts()

掌握了如何发送微信消息后,可以跟玩得要好的朋友试试"消息轰炸机~"(可不敢到处发,⼩⼼被举报、拉⿊,甚⾄封号......)

五、菜单控件的操作

5.1 items()

返回对话框的菜单项,如果没有菜单项,则返回空列表

示例:获取菜单
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
print(menu.items())

结果:

5.2 item_by_index()

语法: item_by_index(idx) 查找索引指定的菜单项

idx :索引,从0开始

复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
menu.item_by_index(0)

5.3 item_by_path()

语法item_by_path(path, exact=False)该方法用于查找路径指定的菜单项。

  • path:指定要选择的菜单项路径,格式为 "MenuItem->MenuItem->MenuItem...",每个 MenuItem 为对应层级的菜单文本;
  • exact:设为True时要求菜单项名称与路径完全匹配,False时允许模糊匹配。

例如:

复制代码
File->Export->ExportAsPNG

空格并不重要,所以你也可以写...

复制代码
File -> Export -> Export As PNG
示例:
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
#menu.item_by_path(path="File->Save").click_input()
menu.item_by_path(path="File -> Save").click_input()

结果:

多层菜单选项的定位

点击 File 打开了 File 窗⼝,点击 Open Recent 会调起 Open Recent 窗⼝解决⽅案:先定位到 Open Recent 窗⼝再对ClearItems选项进⾏操作

复制代码
app = Application(backend="uia").connect(process=25920)
#定位窗⼝ 
win = app.window(title_re=".*Sublime Text.*")
win.wait("visible")
menu_bar = win.child_window(title="应⽤程序", auto_id="MenuBar", 
control_type="MenuBar")
#定位File菜单,并点击 
file_menu = win.child_window(title="File",control_type="MenuItem")
#调起Open Recent窗⼝ 
menu_bar.item_by_path("File->Open Recent").click_input()
#定位到Open Recent窗⼝ 
openRecent_win = file_menu.child_window(title="Open Recent", 
control_type="Window")
openRecent_win.wait("visible")
#定位Open Recent窗⼝Clear Items菜单项 
openRecent_menu = openRecent_win.child_window(title="Open Recent", 
control_type="Menu")
#⽅法⼀:通过菜单操作点击Clear Items 
# clear_items = openRecent_menu.item_by_path("Clear Items")
#click_input不指定坐标会找控件的中间坐标,但是这⾥有偏差 
# clear_items.click_input()
#⽅法⼆:通过定位控件并点击的⽅式 
clear_items = openRecent_win.child_window(title="Clear Items", auto_id="31", 
control_type="MenuItem")
point = clear_items.rectangle().mid_point()
mouse.move(coords=(point.x,point.y+30))
time.sleep(3)
mouse.click(coords=(point.x,point.y+30))

语法menu_select(path, exact=False)该方法用于查找并选择路径指定的菜单项。

  • path:指定菜单项路径,格式为 "MenuItem->MenuItem->MenuItem...",每个 MenuItem 为对应层级的菜单文本;
  • exactTrue要求菜单项名称与路径完全匹配,False允许模糊匹配。

注意:menu_select()需区分两类菜单栏 ------①系统菜单栏(父级为标题栏控件,含 "还原 / 移动 / 最小化" 等标准项);②应用程序菜单栏(多数情况下父级为对话框,是常规操作的目标菜单栏)。

复制代码
File->Export->ExportAsPNG

空格并不重要,所以你也可以写...

复制代码
File -> Export -> Export As PNG
示例:
复制代码
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#win.menu_select(path="File->Save")
win.menu_select(path="File -> Save")

结果:

5.5⾃动化测试场景⽰例:批量创建⽂件

使⽤sublime_text来批量创建⽂件,针对创建⽂件的场景我们进⾏⽤例的设计,并编写对应的测试脚 本 )⽤例设计

2)代码实现以⽤例"验证保存⽂件后,⽂件是否正确写⼊到指定路径。"为例

复制代码
from pywinauto import Application
app = Application(backend='uia').start("D:\software\Sublime Text 
3\sublime_text.exe")
# app = Application(backend='uia').connect(process=28820)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#批量创建五个⽂件 
for i in range(1,3):
 #输⼊内容 
 win.type_keys(f"创建第{i}个⽂件⽂件^s")
 #等待保存⽂件窗⼝ 
 save_win = win['另存为']
 save_win.wait("visible")
 #输⼊⽂件名称 
 filename = f"D:\\file\\GUITest\\test_{i}.txt"
 save_win.child_window(title="⽂件名:", auto_id="1001", 
control_type="Edit").type_keys(filename)
 #点击保存 
 save_win.child_window(title="保存(S)", auto_id="1", 
control_type="Button").click_input()
 
 #检查创建结果 
 time.sleep(1)
 assert os.path.exists(filename)
 
 #创建空⽂件 
 win.type_keys("^n")

六、列表控件的操作

6.1 get_items()

获取列表视图中的所有项⽬。

示例:打印⽂件资源管理列表项

复制代码
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#打印列表项 
print("items:",data_list.get_items())

6.2 item_count()

列表视图中的项数。

复制代码
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#打印列表项数量 
print("items:",data_list.item_count())

6.3 get_item()

返回列表视图中的指定项⽬。 参数: row :可以是⾏的索引

复制代码
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#获取列表中第⼀项 
print("items:",data_list.get_item(row=0))
⾃动化测试场景⽰例:微信发消息(进阶版)

此前实现的微信发消息自动化脚本虽流程完整,但存在明显问题:多次执行时,无法校验发送是否成功,且重复消息易因元素不唯一引发定位报错。结合列表操作优化后,正确思路为:

  1. 动态生成唯一消息文本,避免重复;
  2. 校验发送前后消息列表数量是否增加 1;
  3. 定位消息列表最后一条消息,验证其文本与发送的最新内容一致。

仍然以⽤例"验证用户输⼊⽂本后点击发送按钮,消息是否能成功发送并显⽰在聊天窗⼝中"为例

复制代码
# app = Application(backend="uia").start("D:\software\WeChat\WeChat.exe")
app = Application(backend="uia").connect(process=9768)
win = app.window(title="⽂件传输助⼿",handle=921080)
win.wait("visible")
#获取发送消息之前消息数量 
messageSizeBefore = win.child_window(title="消息", 
control_type="List").item_count()
#找到输⼊控件 
edit = win.child_window(title="输⼊", control_type="Edit")
edit.wait("ready")
#点击消息框 
edit.click_input()
#输⼊⽂本信息--发送当前时间,精确到毫秒 
# 获取当前时间 
now = datetime.now()
edit.type_keys(now)
#点击发送 
win.child_window(title="发送(S)", control_type="Button").click_input()
#检查发送结果 
#1)获取发送消息之前消息数量 
messageSizeAfter = win.child_window(title="消息", 
control_type="List").item_count()
assert messageSizeAfter == messageSizeBefore+1 or 
messageSizeAfter==messageSizeBefore+2
#2)获取发送的消息是否正确------消息内容作为title参数 
win.child_window(title = formatted_time,control_type="ListItem")
相关推荐
小北方城市网几秒前
第 6 课:云原生架构终极落地|K8s 全栈编排与高可用架构设计实战
大数据·人工智能·python·云原生·架构·kubernetes·geo
荒诞硬汉4 分钟前
面向对象(三)
java·开发语言
智航GIS4 分钟前
10.1 网站防爬与伪装策略
python
郝学胜-神的一滴5 分钟前
深入理解Linux中的Try锁机制
linux·服务器·开发语言·c++·程序人生
liliangcsdn5 分钟前
bash中awk如何切分输出
开发语言·bash
2501_941982058 分钟前
企业微信 RPA 自动化:如何解决 UI 异步渲染导致的操作失效?
自动化·企业微信·rpa
belldeep11 分钟前
python:pyTorch 入门教程
pytorch·python·ai·torch
csbysj202012 分钟前
JSON.parse() 方法详解
开发语言
YJlio13 分钟前
Registry Usage (RU) 学习笔记(15.5):注册表内存占用体检与 Hive 体量分析
服务器·windows·笔记·python·学习·tcp/ip·django
奔波霸的伶俐虫14 分钟前
redisTemplate.opsForList()里面方法怎么用
java·开发语言·数据库·python·sql