一、 pywinauto安装和启动
1.安装:
pip install pywinauto
2.backend选择 和 控件查看工具inspect介绍
我们安装好Pywinauto之后,首先要确定哪种可访问性技术(backend)可以用于我们的应用程序,在windows上受支持的有两种:
- Win32 API (
backend= "win32"
) 默认的backend - MS UI Automation (
backend="uia"
)
如果不能确定程序到底适用于那种backend,可以借助于GUI对象检查工具来做,常用的检查工具有Inspect.ex,Spy++ ,下载地址:https://github.com/blackrosezy/gui-inspect-tool
giithub的项目中的inspect好像不行了,可以用下面这个:
链接:https://pan.baidu.com/s/1LHvbcP5NKqSHC7FLSpiTFQ
提取码:p4hm
将inspect左上角的下拉列表中切换到“UI Automation”,然后鼠标点一下你需要测试的程序窗体,inspect就会显示相关信息,如下图所示。说明backend为uia
程序里面的任意一个部位其实都是控件,在inspect的控件树中都可以找到,是一层一层分级别的,可以一个个点开所有控件
2.启动(实例化程序):以微信示例
from pywinauto.application import Application
# 常用方式一:连接已有微信进程(进程号在 任务管理器-详细信息 可以查看,项目中一般根据进程名称自动获取)
app = Application(backend='uia').connect(process=8948)
# 常用方式二:启动微信进程 (注意路径中特殊字符的转义,/和\,不注意有时会出错)
app = Application(backend="uia").start(r'C:\Program Files (x86)\Tencent\WeChat\WeChat.exe')
3.Application对象app的常用方法
通过查看pywinauto的源码中application.py文件,可以看到app的所有属性方法,下面列举常用方法:
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
二、控件定位方法和控件可用方法
操作控件需要以下几个步骤:
第一步 实例化要操作的进程:得到的app是Application对象
第二步 选择窗口 :app.window('一个或多个筛选条件') 得到的窗口是WindowSpecification对象
第三步:基于WindowSpecification对象使用其方法再往下查找,定位到具体的控件
第四步:使用控件的方法属性执行我们需要的操作
WindowSpecification源码中有一些自带的方法可以直接使用,也有注释说到:
WindowSpecification 说明
就是说这是一个查找空间或者窗口的规范,可以使用等待机制。
并且该对象中__getattribute__和__getitem__两个魔术方法,隐式地记录一些私有方法
我的理解是我们可以继续往下一层一层的查找,下面一层一层的控件其实是各种各样的wrapper对象,wrapper有很多种是一系列对象,对象源码都在pywinauto源码的controls目录中
以下总结了常用方法,基本可以满足所有场景的操作,如下:
2.1 层级查找控件的方法
# 通过层级查找控件相关方法+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
window(**kwargs) # 用于窗口的查找
child_window(**kwargs) # 可以不管层级的找后代中某个符合条件的元素,最常用
parent() # 返回此元素的父元素,没有参数
children(**kwargs) # 返回符合条件的子元素列表,支持索引,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 返回子元素的迭代器,是BaseWrapper对象(或子类)
descendants(**kwargs) # 返回符合条件的所有后代元素列表,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 符合条件后代元素迭代器,是BaseWrapper对象(或子类)
2.2 kwargs筛选条件
常用的一些筛选条件:
# 这些是常用的
class_name=None, # 类名
class_name_re=None, # 正则匹配类名
title=None, # 控件的标题文字,对应inspect中Name字段
title_re=None, # 正则匹配文字
control_type=None, # 控件类型,inspect界面LocalizedControlType字段的英文名
best_match=None, # 这个有坑,我不喜欢用,下文有讲解
auto_id=None, # 这个也是固定的可以用,inspect界面AutomationId字段,但是很多控件没有这个属性
源码中有我没用过的条件
2.3 控件可用的方法属性
# 以下几个只支持窗口模式的控件=======================================================================
dlg.close() # 关闭界面
dlg.minimize() # 最小化界面
dlg.maximize() # 最大化界面
dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
dlg.get_show_state() # 正常0,最大化1,最小化2
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() # 鼠标右键单击
# 键盘输入,底层还是调用keyboard.send_keys
ctrl.type_keys(keys, pause = None, with_spaces = False,)
# 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中找到了这个方法,非常有用
#如某些按钮显示值是我们想要的,但是window_text获取到的是固定文字‘修改群昵称’,这个值才是我们修改后的新名字
# 控件常用操作========================================================================================
ctrl.draw_outline(colour='green') # 空间外围画框,便于查看,支持'red', 'green', 'blue'
ctrl.print_control_identifiers(depth=None, filename=None) # 打印其包含的元素,详见打印元素
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
三、具体使用举例
第二节中列举了能用到的方法属性,本节列举实际操作中的具体用法
1.对话框dialog选择
根据pywinauto的源码中application.py文件介绍,窗口选择有三种方式:
View Code
以微信主界面窗口为例:
# 微信主界面几种方式:
# 这个最好用,下面几种不指名道姓容易出错且速度很慢
dlg1 = app.window(class_name='WeChatMainWndForPC') # 是WindowSpecification对象
# 下面几种方法速度慢,我是不喜欢用
# dlg2_1 = app.Dialog
# dlg2_2 = app.微信
# dlg3_1 = app['Dialog']
# dlg3_2 = app['微信']
2.打印元素
我们拿到控件后,是可以将该控件下的所有子控件及其属性以树形结构打印出来的:
# 拿到微信主窗口
win_main_Dialog = app.window(class_name='WeChatMainWndForPC')
# 判断是否为dialog,一个微信是一个dialog,就是窗口
print(win_main_Dialog.is_dialog)
# 给控件画个红色框便于看出是哪个
win_main_Dialog.draw_outline(colour = 'red')
# 打印当前窗口的所有controller(控件和属性)
win_main_Dialog. print_control_identifiers(depth=None, filename=None)
# 源码内部函数名链式赋值了,都能用,一样的
# print_ctrl_ids = dump_tree = print_control_identifiers
depth:打印的深度,缺省时打印最大深度。
filename:将返回的标识存成文件(生成的文件与当前运行的脚本在同一个路径下)
eg:dlg. print_control_identifiers(filename =’a.txt’)
打印出来的文档树就是inspect中的控件树完全展开的样子,都是有层级的,和微信程序中的各个元素是一一对应的:
3 常用查找方法
# 拿到微信主窗口
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")
4 快速定位
定位一个元素我们可以一层一层定位,但是这样真就有点笨蛋了,不仅效率低下还不容易适应结构变化,可以先定位某个页面,打印出页面结构,然后基于页面快速定位
快速定位 伪代码
四、控件自带的的方法
1. 点击和输入
# 左点击,可以点进源码,还有double_click_input,right_click_input等
edit_btn.click_input()
# 先ctrl+a选中所有然后再type_keys替换,和我们选中然后修改一样的
edit_btn.type_keys('^a').type_keys('备注名字', with_spaces=True)
常用快捷键
常规使用很方便,但是有些字符,比如微信中的用户昵称什么的带有表情等特殊符号,用自带的输入方法就会不适用,可以使用keyboard模块(见下)
2.对控件截图并保存
ctrl_qrcode = self.win_login.child_window(title='二维码', control_type='Image')
if ctrl_qrcode.exists():
ctrl_qrcode.capture_as_image().save(img_path)
capture_as_image() 方法 返回控件的其实是 PIL image对象,所以可用该方法的属性方法,比如save
3.窗口的等待
窗口加载需要时间,我们又不能一直sleep就需要等待,等待窗口出现或者等待窗口关闭:
save_dialog.wait('ready',timeout=2)
save_dialog.close()
save_dialog.wait_not('visible')
# 'exists':窗口是有效的句柄
# 'visible':窗口未隐藏,常用
# 'enabled':未禁用窗口
# 'ready':窗口可见并启用,常用
# 'active':窗口处于活动状态
4.窗口存在和关闭
self.chatwnd = wechat.app.window(class_name='ChatWnd')
if self.chatwnd.exists():
self.chatwnd.close()
5.其他
# 顶层窗口
dlg = app.top_window()
# 点方法取值
print(dlg.class_name()) #'WeChatMainWndForPC'
# 滚动 常用于页面的滚动,比如好友列表、聊天列表、消息界面
chat_list.scroll(direction='up', amount='page')
五、鼠标操作
pywinauto自带的鼠标操作有些时候并不能完全满足要求,可以调用mouse的方法
导入:
from pywinauto import mouse
常见操作:
# 移动鼠标
mouse.move(coords=(x, y))
# 指定位置,鼠标左击
mouse.click(button='left', coords=(40, 40))
# 鼠标双击
mouse.double_click(button='left', coords=(140, 40))
# 将属性移动到(140,40)坐标处按下
mouse.press(button='left', coords=(140, 40))
# 将鼠标移动到(300,40)坐标处释放,
mouse.release(button='left', coords=(300, 40))
# 右键单击指定坐标
mouse.right_click(coords=(400, 400))
# 鼠标中键单击指定坐标(很少用的到)
mouse.wheel_click(coords=(400, 400))
# 滚动鼠标 wheel_dist指定鼠标滚轮滑动,正数往上,负数往下。
mouse.scroll(coords=(1200,300),wheel_dist=-3)
示例:
# 以控件中心为起点,滚动
def mouse_scroll(control, distance):
rect = control.rectangle()
cx = int((rect.left+rect.right)/2)
cy = int((rect.top + rect.bottom)/2)
mouse.scroll(coords=(cx, cy), wheel_dist=distance)
mouse_scroll(control=win_main_Dialog.child_window(control_type='List', title='联系人'), distance=-5)
六、键盘操作
和控件自己的type_keys方法效果一样,但是更快,那个是从前到后啪啪啪的输入,这个是一下就出来了那种
在发送文件和图片的时候可以使用键盘模块,复制粘贴,比啪啪啪输入路径再发送速度快多了
并且该模块可以适配很多表情等特殊符号
import keyboard
import io
for line in io.StringIO(msg):
keyboard.write(line.strip()) #
keyboard.send('ctrl+enter')
keyboard.write(chat_name)
keyboard.send('enter')
keyboard.send('ctrl+v')
- 一、 pywinauto安装和启动
- 1.安装:
- 2.backend选择 和 控件查看工具inspect介绍
- 2.启动(实例化程序):以微信示例
- 3.Application对象app的常用方法
- 二、控件定位方法和控件可用方法
- 2.1 层级查找控件的方法
- 2.2 kwargs筛选条件
- 2.3 控件可用的方法属性
- 三、具体使用举例
- 1.对话框dialog选择
- 2.打印元素
- 3 常用查找方法
- 4 快速定位
- 四、控件自带的的方法
- 1. 点击和输入
- 2.对控件截图并保存
- 3.窗口的等待
- 4.窗口存在和关闭
- 5.其他
- 五、鼠标操作
- 六、键盘操作
一、 pywinauto安装和启动
1.安装:
pip install pywinauto
2.backend选择 和 控件查看工具inspect介绍
我们安装好Pywinauto之后,首先要确定哪种可访问性技术(backend)可以用于我们的应用程序,在windows上受支持的有两种:
- Win32 API (
backend= "win32"
) 默认的backend - MS UI Automation (
backend="uia"
)
如果不能确定程序到底适用于那种backend,可以借助于GUI对象检查工具来做,常用的检查工具有Inspect.ex,Spy++ ,下载地址:https://github.com/blackrosezy/gui-inspect-tool
giithub的项目中的inspect好像不行了,可以用下面这个:
链接:https://pan.baidu.com/s/1LHvbcP5NKqSHC7FLSpiTFQ
提取码:p4hm
将inspect左上角的下拉列表中切换到“UI Automation”,然后鼠标点一下你需要测试的程序窗体,inspect就会显示相关信息,如下图所示。说明backend为uia
程序里面的任意一个部位其实都是控件,在inspect的控件树中都可以找到,是一层一层分级别的,可以一个个点开所有控件
2.启动(实例化程序):以微信示例
from pywinauto.application import Application
# 常用方式一:连接已有微信进程(进程号在 任务管理器-详细信息 可以查看,项目中一般根据进程名称自动获取)
app = Application(backend='uia').connect(process=8948)
# 常用方式二:启动微信进程 (注意路径中特殊字符的转义,/和\,不注意有时会出错)
app = Application(backend="uia").start(r'C:\Program Files (x86)\Tencent\WeChat\WeChat.exe')
3.Application对象app的常用方法
通过查看pywinauto的源码中application.py文件,可以看到app的所有属性方法,下面列举常用方法:
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
二、控件定位方法和控件可用方法
操作控件需要以下几个步骤:
第一步 实例化要操作的进程:得到的app是Application对象
第二步 选择窗口 :app.window('一个或多个筛选条件') 得到的窗口是WindowSpecification对象
第三步:基于WindowSpecification对象使用其方法再往下查找,定位到具体的控件
第四步:使用控件的方法属性执行我们需要的操作
WindowSpecification源码中有一些自带的方法可以直接使用,也有注释说到:
WindowSpecification 说明
就是说这是一个查找空间或者窗口的规范,可以使用等待机制。
并且该对象中__getattribute__和__getitem__两个魔术方法,隐式地记录一些私有方法
我的理解是我们可以继续往下一层一层的查找,下面一层一层的控件其实是各种各样的wrapper对象,wrapper有很多种是一系列对象,对象源码都在pywinauto源码的controls目录中
以下总结了常用方法,基本可以满足所有场景的操作,如下:
2.1 层级查找控件的方法
# 通过层级查找控件相关方法+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
window(**kwargs) # 用于窗口的查找
child_window(**kwargs) # 可以不管层级的找后代中某个符合条件的元素,最常用
parent() # 返回此元素的父元素,没有参数
children(**kwargs) # 返回符合条件的子元素列表,支持索引,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 返回子元素的迭代器,是BaseWrapper对象(或子类)
descendants(**kwargs) # 返回符合条件的所有后代元素列表,是BaseWrapper对象(或子类)
iter_children(**kwargs) # 符合条件后代元素迭代器,是BaseWrapper对象(或子类)
2.2 kwargs筛选条件
常用的一些筛选条件:
# 这些是常用的
class_name=None, # 类名
class_name_re=None, # 正则匹配类名
title=None, # 控件的标题文字,对应inspect中Name字段
title_re=None, # 正则匹配文字
control_type=None, # 控件类型,inspect界面LocalizedControlType字段的英文名
best_match=None, # 这个有坑,我不喜欢用,下文有讲解
auto_id=None, # 这个也是固定的可以用,inspect界面AutomationId字段,但是很多控件没有这个属性
源码中有我没用过的条件
2.3 控件可用的方法属性
# 以下几个只支持窗口模式的控件=======================================================================
dlg.close() # 关闭界面
dlg.minimize() # 最小化界面
dlg.maximize() # 最大化界面
dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
dlg.get_show_state() # 正常0,最大化1,最小化2
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() # 鼠标右键单击
# 键盘输入,底层还是调用keyboard.send_keys
ctrl.type_keys(keys, pause = None, with_spaces = False,)
# 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中找到了这个方法,非常有用
#如某些按钮显示值是我们想要的,但是window_text获取到的是固定文字‘修改群昵称’,这个值才是我们修改后的新名字
# 控件常用操作========================================================================================
ctrl.draw_outline(colour='green') # 空间外围画框,便于查看,支持'red', 'green', 'blue'
ctrl.print_control_identifiers(depth=None, filename=None) # 打印其包含的元素,详见打印元素
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
三、具体使用举例
第二节中列举了能用到的方法属性,本节列举实际操作中的具体用法
1.对话框dialog选择
根据pywinauto的源码中application.py文件介绍,窗口选择有三种方式:
View Code
以微信主界面窗口为例:
# 微信主界面几种方式:
# 这个最好用,下面几种不指名道姓容易出错且速度很慢
dlg1 = app.window(class_name='WeChatMainWndForPC') # 是WindowSpecification对象
# 下面几种方法速度慢,我是不喜欢用
# dlg2_1 = app.Dialog
# dlg2_2 = app.微信
# dlg3_1 = app['Dialog']
# dlg3_2 = app['微信']
2.打印元素
我们拿到控件后,是可以将该控件下的所有子控件及其属性以树形结构打印出来的:
# 拿到微信主窗口
win_main_Dialog = app.window(class_name='WeChatMainWndForPC')
# 判断是否为dialog,一个微信是一个dialog,就是窗口
print(win_main_Dialog.is_dialog)
# 给控件画个红色框便于看出是哪个
win_main_Dialog.draw_outline(colour = 'red')
# 打印当前窗口的所有controller(控件和属性)
win_main_Dialog. print_control_identifiers(depth=None, filename=None)
# 源码内部函数名链式赋值了,都能用,一样的
# print_ctrl_ids = dump_tree = print_control_identifiers
depth:打印的深度,缺省时打印最大深度。
filename:将返回的标识存成文件(生成的文件与当前运行的脚本在同一个路径下)
eg:dlg. print_control_identifiers(filename =’a.txt’)
打印出来的文档树就是inspect中的控件树完全展开的样子,都是有层级的,和微信程序中的各个元素是一一对应的:
3 常用查找方法
# 拿到微信主窗口
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")
4 快速定位
定位一个元素我们可以一层一层定位,但是这样真就有点笨蛋了,不仅效率低下还不容易适应结构变化,可以先定位某个页面,打印出页面结构,然后基于页面快速定位
快速定位 伪代码
四、控件自带的的方法
1. 点击和输入
# 左点击,可以点进源码,还有double_click_input,right_click_input等
edit_btn.click_input()
# 先ctrl+a选中所有然后再type_keys替换,和我们选中然后修改一样的
edit_btn.type_keys('^a').type_keys('备注名字', with_spaces=True)
常用快捷键
常规使用很方便,但是有些字符,比如微信中的用户昵称什么的带有表情等特殊符号,用自带的输入方法就会不适用,可以使用keyboard模块(见下)
2.对控件截图并保存
ctrl_qrcode = self.win_login.child_window(title='二维码', control_type='Image')
if ctrl_qrcode.exists():
ctrl_qrcode.capture_as_image().save(img_path)
capture_as_image() 方法 返回控件的其实是 PIL image对象,所以可用该方法的属性方法,比如save
3.窗口的等待
窗口加载需要时间,我们又不能一直sleep就需要等待,等待窗口出现或者等待窗口关闭:
save_dialog.wait('ready',timeout=2)
save_dialog.close()
save_dialog.wait_not('visible')
# 'exists':窗口是有效的句柄
# 'visible':窗口未隐藏,常用
# 'enabled':未禁用窗口
# 'ready':窗口可见并启用,常用
# 'active':窗口处于活动状态
4.窗口存在和关闭
self.chatwnd = wechat.app.window(class_name='ChatWnd')
if self.chatwnd.exists():
self.chatwnd.close()
5.其他
# 顶层窗口
dlg = app.top_window()
# 点方法取值
print(dlg.class_name()) #'WeChatMainWndForPC'
# 滚动 常用于页面的滚动,比如好友列表、聊天列表、消息界面
chat_list.scroll(direction='up', amount='page')
五、鼠标操作
pywinauto自带的鼠标操作有些时候并不能完全满足要求,可以调用mouse的方法
导入:
from pywinauto import mouse
常见操作:
# 移动鼠标
mouse.move(coords=(x, y))
# 指定位置,鼠标左击
mouse.click(button='left', coords=(40, 40))
# 鼠标双击
mouse.double_click(button='left', coords=(140, 40))
# 将属性移动到(140,40)坐标处按下
mouse.press(button='left', coords=(140, 40))
# 将鼠标移动到(300,40)坐标处释放,
mouse.release(button='left', coords=(300, 40))
# 右键单击指定坐标
mouse.right_click(coords=(400, 400))
# 鼠标中键单击指定坐标(很少用的到)
mouse.wheel_click(coords=(400, 400))
# 滚动鼠标 wheel_dist指定鼠标滚轮滑动,正数往上,负数往下。
mouse.scroll(coords=(1200,300),wheel_dist=-3)
示例:
# 以控件中心为起点,滚动
def mouse_scroll(control, distance):
rect = control.rectangle()
cx = int((rect.left+rect.right)/2)
cy = int((rect.top + rect.bottom)/2)
mouse.scroll(coords=(cx, cy), wheel_dist=distance)
mouse_scroll(control=win_main_Dialog.child_window(control_type='List', title='联系人'), distance=-5)
六、键盘操作
和控件自己的type_keys方法效果一样,但是更快,那个是从前到后啪啪啪的输入,这个是一下就出来了那种
在发送文件和图片的时候可以使用键盘模块,复制粘贴,比啪啪啪输入路径再发送速度快多了
并且该模块可以适配很多表情等特殊符号
import keyboard
import io
for line in io.StringIO(msg):
keyboard.write(line.strip()) #
keyboard.send('ctrl+enter')
keyboard.write(chat_name)
keyboard.send('enter')
keyboard.send('ctrl+v')