跳到主要内容

pywinauto uia

使用 UIAWrapper 取得 HwndWrapper

from pywinauto import Application
from pywinauto.uia_element_info import UIAElementInfo
from pywinauto.controls.hwndwrapper import HwndWrapper

# 使用UIA后端启动或连接到应用程序
app = Application(backend="uia").start('notepad.exe')

# 获取UIAWrapper对象
notepad_uia_wrapper = app.window(title='无标题 - 记事本')

# 从UIAWrapper对象获取窗口句柄(HWND)
hwnd = notepad_uia_wrapper.handle

# 使用窗口句柄(HWND)创建HwndWrapper对象
notepad_hwnd_wrapper = HwndWrapper(hwnd)

# 现在你可以使用HwndWrapper对象操作窗口
# 例如,检查窗口是否可见
is_visible = notepad_hwnd_wrapper.is_visible()
print(is_visible) # 输出:True 或 False,取决于窗口状态

control_type 的类型

# 控件类型
edit = 'Edit'
button = 'Button'
list = 'List'
list_item = 'ListItem'

具体参考官网 https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-controlpatternmapping

常用的操作

对话框选择

根据 pywinauto 的源码中 application.py 文件介绍,窗口选择有三种方式:

Once you have an Application instance you can access dialogs in that
application either by using one of the methods below. ::

dlg = app.YourDialogTitle
dlg = app.child_window(title="your title", classname="your class", ...)
dlg = app['Your Dialog Title']

以微信主界面窗口为例:

# 微信主界面几种方式:
# 这个最好用,下面几种不指名道姓容易出错且速度很慢
dlg1 = app.window(class_name='WeChatMainWndForPC') # 是WindowSpecification对象
# 下面几种方法速度慢,我是不喜欢用
# dlg2_1 = app.Dialog
# dlg2_2 = app.微信
# dlg3_1 = app['Dialog']
# dlg3_2 = app['微信']

打印元素

我们拿到控件后,是可以将该控件下的所有子控件及其属性以树形结构打印出来的:

# 拿到微信主窗口
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: 将返回的标识存成文件(生成的文件与当前运行的脚本在同一个路径下)

打印出来的文档树就是 inspect 中的控件树完全展开的样子,都是有层级的,和微信程序中的各个元素是一一对应的:

如果上面的 print_control_identifiers 打印出来的信息不直观,可以使用 print_ctrl_ids

pane_box = main_window.child_window(title="千牛工作台", control_type="Pane", found_index=2)
pane_box.draw_outline(colour="red", thickness=2)
print(pane_box.print_ctrl_ids())

常用查找方法

# 拿到微信主窗口
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")

快速定位

定位一个元素我们可以一层一层定位,但是这样真就有点笨蛋了,不仅效率低下还不容易适应结构变化,可以先定位某个页面,打印出页面结构,然后基于页面快速定位

    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🤗
print(self._popup.Edit0.window_text()) # www.pu🤗
print(self._popup.Edit1.window_text()) # www.pu🤗

print(self._popup.Edit2.window_text()) # qwer1315458571
print(self._popup.child_window(best_match='微信号:Edit').window_text()) # qwer1315458571
print(self._popup.child_window(best_match='Edit2').window_text()) # qwer1315458571

return self._popup.Edit.window_text()
# return self._popup.child_window(title="微信号:", control_type="Text").parent().parent().children()[0].children()[0].window_text()
except:
return None

FindAll 调用特别缓慢的问题

# pywinauto\uia_element_info.py

ptrs_array = self._element.FindAll(tree_scope, cond)

可以发现 UIA 模式下的 FindAll 方法调用特别缓慢,在 UIA 模式下的 FindAll 方法是通过调用 Windows UI Automation API 来查找元素的,

使用 RawViewWalker 查找元素,这个方法是通过遍历整个 UI 树来查找元素的,所以速度会比较慢。

# pywinauto\uia_element_info.py

# FindAll 方法
def find_all(self, tree_scope, cond):
"""
Find all elements that match the condition
"""
# 通过调用 Windows UI Automation API 来查找元素
ptrs_array = self._element.FindAll(tree_scope, cond)
return [UIAElementInfo(ptr) for ptr in ptrs_array]