一、pywinauto

Pywinauto是一款基于Python的跨平台GUI自动化库,专门针对Windows桌面应用程序设计,核心能力在于模拟用户交互行为实现对窗口、对话框及内部空间的精准定位与操作,基于Web的应用程序以及基于自定义渲染引擎的应用程序无法使用Pywinauto实现GUI自动化测试

1. pywinauto常见操作

1.1 打开程序

1.1.1 打开应用程序
start(self, cmd_line......)

cmd_line:是启动应用程序的命令行字符串,必须包含应用程序的路径和名称,还可以包含启动参数

1.1.2 连接已经打开的应用程序
connect(self, **kwargs)

部分参数详解:

process:目标的进程ID

handle:目标的窗口句柄

注意:连接到已在运行的进程,该动作仅根据一个参数执行

#打开应⽤程序
app = Application(backend="uia").start("D:\software\Sublime Text
3\sublime_text.exe")

#连接已经打开的应⽤程序
#通过pid连接
#获取应⽤程序对应的pid
#app = Application(backend="uia").connect(process=app.process)
app = Application(backend="uia").connect(process=31528)

#通过句柄连接
app = Application(backend="uia").connect(handle=65552)

1.2 定位窗口

方法一:通过高级pywinauto API提供的window方法来定位

app.window(title='',...)

参数说明(参数可以组合使用):

  • title:文本为指定值的元素
  • title_re:文本匹配指定正则表达式的元素
  • best_match:标题与指定值相似的元素
  • class_name:窗口类为指定值的元素
  • class_name_re:类名匹配指定正则表达式的元素
#start参数换成Sublime Text的⽬标路径
app = Application(backend='uia').start("D:\software\Sublime Text
3\sublime_text.exe")

win = app.window(title_re='.*Sublime Text.*')

#title--精确匹配
win = app.window(title="untitled • - Sublime Text (UNREGISTERED)")

#title_re-正则匹配
win = app.window(title_re=".*Sublime.*")

#best_match-模糊匹配
win = app.window(best_match="untitled • - Sublime Text")

#class_name--精确匹配
win = app.window(class_name="PX_WINDOW_CLASS")

#class_name--正则匹配
win = app.window(class_name_re=".*WINDOW_CLASS")

win.print_control_identifiers()

print_control_identifiers()方法用于打印窗口及其子控件的标识符信息,帮助用户识别控件

输出的内容:

  • 控件的类名、标题、位置(上下左右的边界的坐标值、控制类型等信息)
  • 每个控件的“best match”名称列表,这些名称可以用于引用控件
Dialog - 'untitled • - Sublime Text (UNREGISTERED)' (L806, T81, R1811,
B1007)
['Dialog', 'untitled • - Sublime Text (UNREGISTERED)Dialog', 'untitled • -
Sublime Text (UNREGISTERED)']
child_window(title="untitled • - Sublime Text (UNREGISTERED)",
control_type="Window")
|
| ScrollBar - '' (L815, T44, R915, B54)
| ['ScrollBar', 'ScrollBar0', 'ScrollBar1']
| |
| | Button - '左移⼀列' (L0, T0, R0, B0)
| | ['左移⼀列', 'Button', '左移⼀列Button', 'Button0', 'Button1']
| | child_window(title="左移⼀列", auto_id="UpButton",
control_type="Button")
| |
| | Thumb - '位置' (L0, T0, R0, B0)
| | ['Thumb', '位置', '位置Thumb', 'Thumb0', 'Thumb1', '位置0', '位置1',
'位置Thumb0', '位置Thumb1']
| | child_window(title="位置", auto_id="ScrollbarThumb",
control_type="Thumb")
| |
| | Button - '右移⼀列' (L0, T0, R0, B0)
| | ['右移⼀列Button', '右移⼀列', 'Button2']
| | child_window(title="右移⼀列", auto_id="DownButton",
control_type="Button")
|
| ScrollBar - '' (L715, T144, R725, B244)
| ['ScrollBar2']
| |
| | Button - '上⼀⾏' (L0, T0, R0, B0)
| | ['上⼀⾏', '上⼀⾏Button', 'Button3']
| | child_window(title="上⼀⾏", auto_id="UpButton",
control_type="Button")
| |
| | Thumb - '位置' (L0, T0, R0, B0)
| | ['Thumb2', '位置2', '位置Thumb2']
| | child_window(title="位置", auto_id="ScrollbarThumb",
control_type="Thumb")
| |
| | Button - '下⼀⾏' (L0, T0, R0, B0)
| | ['下⼀⾏', '下⼀⾏Button', 'Button4']
| | child_window(title="下⼀⾏", auto_id="DownButton",
control_type="Button")
|
| TitleBar - '' (L835, T84, R1802, B119)
| ['TitleBar']
| |
| | Menu - '系统' (L652, T72, R674, B94)
| | ['Menu', '系统Menu', '系统', '系统0', '系统1', 'Menu0', 'Menu1']
| | child_window(title="系统", auto_id="MenuBar", control_type="MenuBar")
| | |
| | | MenuItem - '系统' (L652, T72, R674, B94)
| | | ['系统2', 'MenuItem', '系统MenuItem', 'MenuItem0', 'MenuItem1']
| | | child_window(title="系统", control_type="MenuItem")
| |
| | Button - '最⼩化' (L1624, T82, R1684, B119)
| | ['最⼩化Button', '最⼩化', 'Button5']
| | child_window(title="最⼩化", control_type="Button")
| |
| | Button - '最⼤化' (L1684, T82, R1743, B119)
| | ['最⼤化', 'Button6', '最⼤化Button']
| | child_window(title="最⼤化", control_type="Button")
......
......

方法二:通过动态解析对象属性定位

win = app.Dialog
#上⾯写法等价于
win = app.window(best_match='Dialog')

Python通过动态解析对象属性简化了创建窗口规范,但是存在不具备唯一性和稳定性的问题

1.3 窗口操作

方法功能描述返回值
close()关闭窗口无返回值
maximize()最大化窗口
minimize()最小化窗口
restore()窗口恢复正常大小
get_show_state()获取窗口的显示状态返回一个整数值表示窗口的状态
is_dialog()检查控件是否是顶级窗口返回布尔值
is_maximized()检查窗口是否处于最大化状态返回布尔值
is_minimized()检查窗口是否处于最小化状态返回布尔值
is_normal()检查窗口是否处于原始状态(未大未小)返回布尔值

1.4 定位控件

1.4.1 控件分类

打开UISpy,通过ControlType可查看所定位到控件的类型,常见控件实例:

分类空间名称说明
窗⼝与对话框
对话框 (Dialog)
⽤于与⽤⼾交互,如显⽰警告、确认操作或输⼊信息。
窗格 (Pane)
通常作为窗⼝的⼀部分,⽤于显⽰特定内容或功能。
输⼊控件
按钮 (Button)
⽤于触发操作,如点击按钮执⾏某个功能。
编辑栏 (Edit)
⽤于输⼊或编辑⽂本,⽀持多⾏或单⾏输⼊。
组合框 (ComboBox)
结合⽂本框和列表框,⽤⼾可以选择预定义选项或输入自定义值。
列表框 (ListBox)
⽤于显⽰可选择的项⽬列表,⽀持单选或多选。
菜单控件
菜单 (Menu)
⽤于提供功能选项,通常位于窗⼝顶部。
菜单项 (MenuItem)
菜单中的具体选项,点击后执⾏对应功能。
弹出菜单 (PopupMenu)
右键单击时弹出的菜单,⽤于快速访问常⽤功能。
列表显⽰控件
列表显⽰控件 (ListView)
⽤于以表格形式显⽰数据,⽀持多列显⽰和排序。
容器控件
组框 (GroupBox)
⽤于对控件进⾏分组,提⾼界⾯的可读性和组织性。
选择控件
复选框 (CheckBox)
⽤于多选操作,⽤⼾可以勾选多个选项。
单选框 (RadioButton)
⽤于互斥选择,⽤⼾只能选择⼀个选项。
显⽰控件
状态栏 (StatusBar)
通常位于窗⼝底部,⽤于显⽰应⽤状态或提⽰信息。
静态内容 (Static)
⽤于显⽰静态⽂本或图像,通常不可编辑。
导航控件
树状视图 (Tree View)
⽤于展⽰分层数据,如⽂件夹结构或组织结构。
选项卡控件 (TabControl)
⽤于在有限空间内组织多个⻚⾯或选项卡,每个选项卡可以包含不同的内容。
⼯具控件
⼯具栏 (Toolbar)
⽤于放置常⽤按钮或⼯具,⽅便⽤⼾快速操作。
⼯具提⽰ (ToolTips)
当⿏标悬停在控件上时显⽰提⽰信息,帮助⽤⼾理解控件功能。
头部内容
头部内容 (Header)
通常⽤于显⽰标题或表头信息,如表格的列标题。

定位控件,需要借助前面学到的print_control_identifiers()方法,打印窗口及其子空间的标识符信息

1.4.2 动态解析

基于best_match标题来定位

from pywinauto import Application

# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)

# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')

# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")

# 通过窗⼝的标题获取菜单对象
menu = win['应⽤程序']

# 打印菜单项的列表,查看菜单中包含的所有选项
print(menu.items())
1.4.3 child_window

child_window()和定位窗口的方法window()参数一样,可以通过标题或者类名进行精确匹配、模糊匹配等,额外拓展几个进行空间定位时需要用到的参数:

属性名称描述
control_type

具有此控件类型的元素(字符串)

auto_id具有此自动化ID的元素
found_index返回的已过滤子元素的索引,下标从0开始
from pywinauto import Application

# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)

# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')

# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")

# 通过控件的属性(标题、⾃动ID和控件类型)定位菜单栏对象
menu = win.child_window(title="应⽤程序", auto_id="MenuBar",
control_type="MenuBar")

# 打印菜单项的列表,查看菜单中包含的所有选项
print(menu.items())

若存在多个控件的auto_id相同,可通过添加索引参数来定位

from pywinauto import Application

# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)

# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')

# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")

#存在相同的auto_id和control_type,使⽤found_index来定位
menu = win.child_window(auto_id="MenuBar",
control_type="MenuBar",found_index=1)
print(menu.texts())

注意,若元素值为空,或不是唯一的情况下,都会导致定位失败

控件之间存在父子关系,可通过children()方法获取控件的子类,也可以通过parent()方法获取控件的父类

1.5 等待

GUI应用程序行为通常不稳定,脚本需要等待,直到出现新窗口或关闭现有窗口等

1.5.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:表示选择的窗口状态
  1. exists:表示窗口是一个有效的句柄
  2. visible:表示窗口不隐藏,可见
  3. enable:表示窗口未被禁用,可操作
  4. ready:表示窗口可见且已启用
  5. active:表示窗口处于活动状态
  • timeout:表示超时
  • retry_interval:表示重试时间间隔
1.5.2 wait_until

等待满足某个条件

wait_until(timeout,retry_interval,func,value=True,op=operator.eq,*args, **kwargs)
  • Timeout:超时时间
  • retry_interval:重试时间
  • func:执行的函数
  • value:比较的值
i=0
def work():
  global i
  i += 1
  print("当前i的值为",i)
  return i
#等待work返回的结果为5,继续往下执⾏
wait_until(10,1,work,5)
print("等待通过")

1.6 控件的操作

1.6.1 点击
  • click_input():模拟鼠标左键单击操作
  • right_click_input():模拟鼠标右键单机操作
  • double_click_input():模拟鼠标右键双击操作
1.6.2 文本
  • texts():用于获取窗口或控件中的所有文本内容,返回一个列表,其中每个元素是一个字符串,表示窗口或空间中的某个文本片段
  • window_text():用于获取窗口或控件的主文本内容,通常是指窗口的标题或主要显示的文本,返回一个字符串

1.7 鼠标操作

示例:鼠标点击

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))

示例:拖动滚动条

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)
  • 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模块常见操作:

方法功能描述
mouse.click(button='left', coords=(0, 0))
单击指定的坐标
mouse.scroll(coords=(0, 0), wheel_dist=1)
滚动⿏标滚轮,wheel_dist表⽰滚动的 距离,⼤于0是向上滚动,⼩于0是向下滑动。
mouse.double_click(button='left',coords=(0, 0))
双击指定的坐标
mouse.right_click(coords=(0, 0))
右键单击指定的坐标
mouse.move(coords=(0, 0))
移动⿏标到指定坐标
mouse.wheel_click(coords=(0, 0))
⿏标中键单击指定的坐标
mouse.press(button='left', coords=(0, 0))
按下⿏标按钮
mouse.release(button='left', coords=(0, 0))
释放⿏标按钮
# 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)

1.8 键盘操作

keyborad.send_keys()是一个通用的键盘输入方法,可以直接将案件序列发送到当前具有焦点的窗口,但在实际的自动化场景中,通常需要针对特定的控件进行精确的输入操作,为了满足这种需求,pywinauto引入了type_keys方法,这是控件对象的专属方法,可以直接作用于指定的控件

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

直接输入文本:

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)
1.8.2 按键

使用{}格式表示虚拟键码:

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("1+2=3") # 错误:`+` 会被识别为 Shift
win.type_keys("1{+}2=3") # 正确

1.9 菜单控件的操作

1.9.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())
1.9.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)
1.9.3 item_by_path()

语法:item_by_path(path, exact=false)用户查找路径指定的菜单项

  • path:用于指定要选择的菜单项路径
  • exact:设置为true,则要求菜单项名称与路径中的名称完全匹配;为false,则允许模糊匹配
1.9.4 menu_select()

语法:menu_select(path, exact=false)

1.10 列表控件的操作

1.10.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())
1.10.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())
1.10.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))

更多推荐