赞
踩
==Pywinauto==是基于Python开发的,用于自动化测试的脚本模块,主要操作于Windows标准图形界面。它可以允许你很容易的发送鼠标、键盘动作给Windows的对话框和控件。
参考:https://www.cnblogs.com/xp1315458571/p/13892205.html
命令行安装:pip install pywinauto,简单,方便,直接。
手动安装:【解压缩后执行 python setup.py install】
检查安装成功:使用python解释器运行以下代码,启动windows自带的记事本,如果正常启动不报错,则安装成功。
from pywinauto.application import Application
app = Application(backend="uia").start("notepad.exe")
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nDRJJdlR-1649085284382)(images/siOJTqTWNys4zVRoDyqDqKnqnsjRccIeCLmSVO0Y-fs.png)]](https://img-blog.csdnimg.cn/00b911e25f3e4d28b762ec30967ec9a0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5L2g5Lus55qE5aW95pyL5Y-L5aSn5by6,size_15,color_FFFFFF,t_70,g_se,x_16)
安装完成后,要先确定哪种==可访问性技术(backend)==可以用于我们的应用程序。在windows上受支持的有两种:
backend= "win32") ,默认的backendbackend="uia")注:可以借助于GUI对象检查工具来确定程序到底适用于那种backend。eg:如果使用 inspect 的uia模式,可见的控件和属性更多的话,backend可选uia,反之,backend可选win32。
常用的检查工具:
1. Inspect(定位元素工具(uia))
2. Spy++ (定位元素工具(win32))
3. UI Spy (定位元素工具)
4. Swapy(可简单生成pywinauto代码)
下载:https://github.com/blackrosezy/gui-inspect-tool,或 https://www.pconlife.com/download/otherfile/128053/5f2da0a792d68ff9e9bf33a60613fb57/,或
https://pan.baidu.com/s/1LHvbcP5NKqSHC7FLSpiTFQ,提取码:p4hm
使用:将inspect左上角的下拉列表中切换到==“UI Automation”,然后鼠标点一下需要测试的程序窗体==,右侧就会显示相关信息,如图所示,说明backend为uia。
注意:程序里面的任意一个部位其实都是控件,在inspect的控件树中都可以找到,是一层一层分级别的,可以一个个点开所有控件。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWSDQFsC-1649085284385)(images/uB8T9ASKbemXPhs6UqlIn4KpNNKirrk8hLXFQF7KO1g.png)]](https://img-blog.csdnimg.cn/8274bc1a3ce44d54ad82c37096df806d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5L2g5Lus55qE5aW95pyL5Y-L5aSn5by6,size_15,color_FFFFFF,t_70,g_se,x_16)
from pywinauto.application import Application
启动:start(self, cmd_line, timeout=app_start_timeout),timeout为超时参数,默认为5s,如果启动时间较长可设置 timeout。
举例:
app = Application(backend = "uia").start(r"E:\Office\Office14\EXCEL.exe") # 启动Excel
app = Application(backend = "uia").start(r'C:\Program Files (x86)\Tencent\WeChat\WeChat.exe') # 启动微信
连接:connect()用于连接已经启动的程序。连接一个已经运行的程序有以下几种方法:
(1). 使用进程号:进程号在 任务管理器-详细信息 可以查看,项目中一般根据进程名称自动获取
(2). 使用handle:应用程序的窗口句柄
(3). path:进程的执行路径。GetModuleFileNameEx 模块会查找进程的每一个路径并与我们传入的路径去做比较
(4). 参数组合:定位元素的参数,传递给pywinauto.findwindows.find_elements()这个函数
app = Application(backend='uia').connect(process=8948) # 进程号
app = Application().connect(handle=0x010f0c) # 句柄
app = Application().connect(path="D:\Office14\EXCEL.exe") # path
app = Application().connect(title_re=".*Notepad", class_name=“Notepad”) # 参数组合
注意:应用程序必须先准备就绪,才能使用connect(),当应用程序start()后没有超时和重连的机制。在pywinauto外再启动应用程序,需要sleep,等程序start。
app.top_window() # 返回应用程序当前顶部窗口,是WindowSpecification对象,可以继续使用对象的方法往下继续查找控件
# eg:如:app.top_window().child_window(title='地址和搜索栏', control_type='Edit')
app.window(**kwargs) # 根据筛选条件,返回一个窗口, 是WindowSpecification对象,可以继续适用对象的方法往下继续查找控件
# eg: 微信主界面 app.window(class_name='WeChatMainWndForPC')
app.windows(**kwargs) # 根据筛选条件返回一个窗口列表,无条件默认全部,列表项为wrapped(装饰器)对象,可以使用wrapped对象的方法,注意不是WindowSpecification对象
# eg:[<uiawrapper.UIAWrapper - '李渝的早报 - Google Chrome', Pane, -2064264099699444098>]
app.kill(soft=False) # 强制关闭
app.cpu_usage() # 返回指定秒数期间的CPU使用率百分比
app.wait_cpu_usage_lower(threshold=2.5, timeout=None, usage_interval=None) # 等待进程CPU使用率百分比小于指定的阈值threshold
app.is64bit() # 如果操作的进程是64-bit,返回True
层级查找控件的方法:定位控件
# 通过层级查找控件相关方法
window(**kwargs) # 用于窗口的查找
child_window(**kwargs) # 可以无视层级的找后代中某个符合条件的元素===>【最常用】
parent() # 返回此元素的父元素,没有参数
children(**kwargs) # 返回符合条件的子元素列表,支持索引,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 返回子元素的迭代器,是BaseWrapper对象(或子类)
descendants(**kwargs) # 返回符合条件的所有后代元素列表,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 符合条件后代元素迭代器,是BaseWrapper对象(或子类)---> 存疑,是iter_descendants?
kwargs筛选条件:
# 常用的 class_name=None, # 类名 class_name_re=None, # 正则匹配类名 title=None, # 控件的标题文字,对应inspect中Name字段 title_re=None, # 正则匹配文字 control_type=None, # 控件类型,inspect界面LocalizedControlType字段的英文名 best_match=None, # 模糊匹配类似的title auto_id=None, # inspect界面AutomationId字段,但是很多控件没有这个属性 # 不常用 parent=None, process=None,# 这个基本不用,每次启动进程都会变化 top_level_only=True, visible_only=True, enabled_only=False, handle=None, ctrl_index=None, found_index=None, predicate_func=None, active_only=False, control_id=None, framework_id=None, backend=None,
控件可用的方法属性:
# 以下几个只支持窗口模式的控件 dlg.close() # 关闭界面 dlg.minimize() # 最小化界面 dlg.maximize() # 最大化界面 dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面 dlg.get_show_state() # 正常0,最大化1,最小化2 dlg.menu_select() # 菜单栏,eg:app.window.menu_select(Edit -> Replace) dlg.exists(timeout=None, retry_interval=None) # 判断是否存在 #timeout:等待时间,一般默认5s #retry_interval:timeout内重试时间 dlg.wait(wait_for, timeout=None, retry_interval=None) # 等待窗口处于特定状态 dlg.wait_not(wait_for_not, timeout=None, retry_interval=None) # 等待窗口不处于特定状态,即等待消失 # wait_for/wait_for_not: # * 'exists' means that the window is a valid handle # * 'visible' means that the window is not hidden # * 'enabled' means that the window is not disabled # * 'ready' means that the window is visible and enabled # * 'active' means that the window is active # timeout:等待多久 # retry_interval:timeout内重试时间 # eg: dlg.wait('ready') # 鼠标键盘操作,只列举了常用形式,他们有很多默认参数但不常用,可以在源码中查看 ctrl.click_input() # 最常用的点击方法,一切点击操作的基本方法(底层调用只是参数不同),左键单击,使用时一般都使用默认不需要带参数 ctrl.right_click_input() # 鼠标右键单击 ctrl.type_keys(keys, pause = None, with_spaces = False,) # 键盘输入,底层还是调用keyboard.send_keys # keys:要输入的文字内容 # pause:每输入一个字符后等待时间,默认0.01就行 # with_spaces:是否保留keys中的所有空格,默认去除0 ctrl.double_click_input(button ="left", coords = (None, None)) # 左键双击 ctrl.press_mouse_input(coords = (None, None)) # 指定坐标按下左键,不传坐标默认左上角 ctrl.release_mouse_input(coords = (None, None)) # 指定坐标释放左键,不传坐标默认左上角 ctrl.move_mouse_input(coords=(0, 0)) # 将鼠标移动到指定坐标,不传坐标默认左上角 ctrl.drag_mouse_input(dst=(0, 0)) # 将ctrl拖动到dst,是press-move-release操作集合 # 控件的常用属性 ctrl.children_texts() # 所有子控件的文字列表,对应inspect中Name字段 ctrl.window_text() # 控件的标题文字,对应inspect中Name字段 # ctrl.element_info.name ctrl.class_name() # 控件的类名,对应inspect中ClassName字段,有些控件没有类名 # ctrl.element_info.class_name ctrl.element_info.control_type # 控件类型,inspect界面LocalizedControlType字段的英文名 ctrl.is_child(parent) # ctrl是否是parent的子控件 ctrl.legacy_properties().get('Value') # 可以获取inspect界面LegacyIAccessible开头的一系列字段,在源码uiawraper.py中找到了这个方法,非常有用 # 控件常用操作 ctrl.draw_outline(colour='green') # 空间外围画框,便于查看,支持'red', 'green', 'blue' ctrl.print_control_identifiers(depth=None, filename=None) # 以树形结构打印其包含的元素,详见打印元素 # depth:打印的深度,缺省时打印最大深度。 # filename:将返回的标识存成文件(生成的文件与当前运行的脚本在同一个路径下) ctrl.scroll(direction, amount, count=1,) # 滚动 # direction :"up", "down", "left", "right" # amount:"line" or "page" # count:int 滚动次数 ctrl.capture_as_image() # 返回控件的 PIL image对象,可继续使用其方法如下: # eg: ctrl.capture_as_image().save(img_path) ret = ctrl.rectangle() # 控件上下左右坐标,(L430, T177, R1490, B941),可输出上下左右 # eg: ret.top=177 # ret.bottom=941 # ret.left=430 # ret.right=1490
对话框dialog选择:以微信主界面窗口为例:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fh4zGIAN-1649085284388)(images/kKNP7NbpsgB16AzHJ4-e9RVr6WptSPgfzaeJB7r4g1g.png)]](https://img-blog.csdnimg.cn/4480635d685c4e76b018b916759001c0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5L2g5Lus55qE5aW95pyL5Y-L5aSn5by6,size_13,color_FFFFFF,t_70,g_se,x_16)
# 微信主界面几种方式:
# 这个最好用
dlg1 = app.window(class_name='WeChatMainWndForPC') # 是WindowSpecification对象
# 下面几种方法速度慢,不指名道姓容易出错
dlg2 = app.Dialog # eg:dlg2 = app.微信
dlg3 = app['Dialog'] # eg: dlg3 = app['微信']
打印元素:拿到控件后,可以将该控件下的所有子控件及其属性以树形结构打印出来
# 拿到微信主窗口 win_main_Dialog = app.window(class_name='WeChatMainWndForPC') # 判断是否为dialog,一个微信是一个dialog,就是窗口 print(win_main_Dialog.is_dialog) # 给控件画个红色框便于看出是哪个 win_main_Dialog.draw_outline(color = 'red') # 打印当前窗口的所有controller(控件和属性) win_main_Dialog.print_control_identifiers(depth=None, filename=None) # depth:打印的深度,缺省时打印最大深度 # filename:将返回的标识存成文件(生成的文件与当前运行的脚本在同一个路径下) # eg:dlg. print_control_identifiers(filename =’a.txt’) # 源码内部函数名链式赋值了,都能用,一样的 # print_ctrl_ids = dump_tree = print_control_identifiers
注:打印出来的文档树就是inspect中的控件树完全展开的样子,都是有层级的,和微信程序中的各个元素是一一对应的:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABSGqIfJ-1649085284390)(images/76d5Z-JdZHiHYmQNFYMXjKycYlJj_UgTKmkti30maJU.png)]](https://img-blog.csdnimg.cn/65e3cf8050654dffa4c63b9f54325d25.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5L2g5Lus55qE5aW95pyL5Y-L5aSn5by6,size_20,color_FFFFFF,t_70,g_se,x_16)
常用查找方法:
win_main_Dialog = app.window(class_name='WeChatMainWndForPC') # 拿到微信主窗口 chat_list = win_main_Dialog.child_window(control_type='List', title='会话') # 主窗口下的某个窗口,不管层级的找 first = chat_list.items()[0] # 第一个聊天项,列表支持items(),支持循环,支持索引 # 详情页修改备注操作 parent()和children()都是只往上或往下查找一个层级,所有满足的放进列表 details_page = win_main_Dialog.child_window(class_name='ContactProfileWnd') # 窗口下的某个窗口 we_id = details_page.child_window(title="微信号:", control_type="Text").parent().children()[1].window_text() # 窗口的爸爸的第二个儿子的文字 alia = details_page.child_window(title="微信号:", control_type="Text").parent().parent().children()[0].children()[0].window_text() edit_btn = details_page.child_window(title="备 注", control_type="Text").parent().children()[1] edit_btn.click_input() btn_modify_name_edit = edit_btn # 先ctrl+a选中所有然后再type_keys替换 btn_modify_name_edit.type_keys('^a').type_keys('备注名字', with_spaces=True) # descendants查找所有后代中满足的,不管层级,所有满足的放进列表 btns_list = win_main_Dialog.child_window(control_type='ToolBar').parent().descendants(control_type='Button') btns_list[0].click_input() dialog.child_window(title="文件名(N):", auto_id="1148", control_type="Edit")
快速定位:可以先定位某个页面,打印出页面结构,然后基于页面快速定位
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tANK5LOB-1649085284392)(images/vySP22-nHSDal22xmYZovHfF7_IJASjkTmarEWfkGrE.png)]](https://img-blog.csdnimg.cn/0f62d057d57d4640a87f94d5e43936f4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5L2g5Lus55qE5aW95pyL5Y-L5aSn5by6,size_20,color_FFFFFF,t_70,g_se,x_16)
# 快速定位 伪代码
def we_name(self):
# todo+++++++++++++++++++++++++++++++++++++
try:
self._popup = wechat.win_main.child_window(class_name='ContactProfileWnd')
self._popup.wait('visible')
self._popup.print_control_identifiers(depth=None, filename=None)
print(self._popup.Edit.window_text()) # www.pu声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。