Python 第三方模块之 uiautomation - UI自动化编程

0、简介

  • 对于常规的模拟鼠标和键盘操作,我们会使用 pyautogui
    uiautomation 模块不仅支持这些操作,还能通过控件定位方式直接定位到目标控件的位置,不需要自己去获取对应坐标位置。
    uiautomation 模块不仅支持任意坐标位置截图,还支持目标控件的截图,缺点在于截取产生的图片对象难以直接与PIL库配合,只能导出文件后让PIL图像处理库重新读取。
    对于能够获取到其ScrollItemPattern对象的控件还可以通过ScrollIntoView方法进行视图定位,与游览器的元素定位效果几乎一致。

  • 对于常规的热键功能,我们会使用 pynput
    但 uiautomation 模块的热键注册比 pynput 更简单功能更强。uiautomation 模块所支持的剪切板操作的功能也远远超过常规的专门用于剪切板复制粘贴的库。
    而且 uiautomation 模块能直接让你的 python 程序实现提权。

  • 作者写的博客:https://www.cnblogs.com/Yinkaisheng/p/3444132.html

  • uiautomation 地址:https://github.com/yinkaisheng/Python-UIAutomation-for-Windows

  • uiautomation 中文介绍:https://github.com/yinkaisheng/Python-UIAutomation-for-Windows/blob/master/readme_cn.md

uiautomation是yinkaisheng业余时间开发一个模块。封装了微软UIAutomation API,支持自动化Win32,MFC,WPF,Modern UI(Metro UI), Qt, IE, Firefox( version<=56 or >=60), Chrome浏览器和基于Electron开发的应用程序(加启动参数–force-renderer-accessibility也能支持UIAutomation被自动化).

uiautomation只支持Python 3版本,依赖comtypes和typing这两个包,但Python不要使用3.7.6和3.8.1这两个版本,comtypes在这两个版本中不能正常工作(issue)。

0.1、安装

1
pip install uiautomation

1、涉及工具

  1. 使用 uiiautomation 需要配合应用程序元素定位辅助工具,如:inspect.exe, Insight.exe, UIspy.exe。
    Insight.exe:https://accessibilityinsights.io/
  2. pyautogui【技术不到位,有些元素没法精确定位,用uiautomation获取旁边元素的位置坐标,再用这个模拟键鼠操作】

1.1、UIspy.exe 简单使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Identification
ClassName: "WeChatMainWndForPC" # ClassName,查找窗口的时候用
ControlType: "ControlType.Window" # ControlType,控件类型,查找窗口的时候 不同控件类型,用不同方法
Culture: "(null)"
AutomationId: ""
LocalizedControlType: "窗口"
Name: "微信" # Name,有些程序有,有些没有
ProcessId: "10396 (WeChat)"
RuntimeId: "42 10162140"
IsPassword: "False"
IsControlElement: "True"
IsContentElement: "True"

Visibility
BoundingRectangle: "(2108, 523, 1365, 924)" # BoundingRectangle,边界矩形,显示这个窗口或按钮、标签、文本框 的 位置信息【左上x坐标,左上y坐标,右下x坐标,右下y坐标】
ClickablePoint: "(null)"
IsOffscreen: "False"

2、uiautomation

2.1、uiautomation 查找窗口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import uiautomation


# WindowContrl查找窗口中的程序,如果有中文则需用Unicode;
# searchFromControl 从哪个控件开始查找,如果为 None,从根节点 Desktop 开始查找
# searchDepth 搜索深度,在指定的深度范围内(包括1~searchDepth层中的所有子孙控件)搜索第一个满足搜索条件的控件
# searchInterval 搜索间隔,每次搜索之后间隔多长时间再搜索,超时时间为 TIME_OUT_SECOND
# ClassName 类名字
# Name 控件名字
# SubName 控件部分名字
# RegexName 控件名字,支持正则表达式
# Depth 控件相对于searchFromControl的精确深度,只在Depth所在的深度(如果Depth>1,排除1~searchDepth-1层中的所有子孙控件)搜索第一个满足搜索条件的控件
uiautomation.WindowContrl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找编辑窗口位置,找到后可用DoubleClick()来改变电脑的focus;edit.SetValue(“string”)输入值
uiautomation.EditControl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找菜单按钮
uiautomation.MenuItemControl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找下拉框,然后在此基础上用Select(“name”)方法来选择需要的选项
uiautomation.ComboBoxControl(searchFromControl, searchDepth, searchInterval, **searchProperties)
# 查找单选
uiautomation.CheckBoxControl
# 查找按钮
uiautomation.BottonControl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找文本
uiautomation.TextControl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找文档
uiautomation.DocumentControl(searchFromControl, searchDepth, searchInterval, **searchProperties)


# 查找日历
uiautomation.CalendarControl(searchFromControl, searchDepth, searchInterval, **searchProperties)
  • 如果 ControlType 为 "ControlType.Window",那么捕获窗口就用:uiautomation.WindowControl
  • 如果 ControlType 为 "ControlType.Text",那么捕获窗口就用:uiautomation.TextControl
  • 如果 ControlType 为 "ControlType.Xxx",那么捕获窗口就用:uiautomation.XxxControl
  • 依次类推…
  • 如果类型不明确,则使用uiautomation.Control

2.2、Uiautomation 窗口操作

1
2
3
4
5
6
7
8
9
10
11
import uiautomation as auto

window = auto.WindowControl(ClassName="CabinetWClass",searchDepth=1) #控制面板窗口
window.Exists(maxSearchSeconds) # 判断此窗口是否存在
window.SwitchToThisWindow() # 激活指定窗口(无论是否最小化)
window.SetActive() # 使用
window.SetTopMost() # 设置为顶层
window.ShowWindow(uiautomation.ShowWindow.Maximize) # 窗口最大化
window.Maximize() # 窗口最大化
window.CaptureToImage("a.png") # 截图;
window.Close() # 关闭窗口;

2.3、Uiautomation 传值

1
2
SendKeys(key: int, waitTime: float = OPERATION_WAIT_TIME)  # 如果已在编辑位置,则可用此方法来输入值,{Ctrl}为ctrl键,其他类似;{@8}格式可输入8个@,对于数字也可实现此功能,但对于字母不能…
SetValue() # 传值

2.4、Uiautomation 对 windows 键盘操作

1
2
3
4
5
# 按住Ctrl
uiautomation.Win32API.PressKey(uiautomation.Keys.VK_CONTROL)

# 释放Ctrl
uiautomation.Win32API.ReleaseKey(uiautomation.Keys.VK_CONTROL)

2.5、Uiautomation 鼠标操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 单击鼠标左键,模拟鼠标在点 x, y 的点击。
# OPERATION_WAIT_TIME默认为 0.5,即等待时间默认为0.5
Click(x: int, y: int, waitTime: float = OPERATION_WAIT_TIME)


# 单击鼠标中键,模拟鼠标在点 x, y 的点击。
# OPERATION_WAIT_TIME默认为 0.5,即等待时间默认为0.5
MiddleClick(x: int, y: int, waitTime: float = OPERATION_WAIT_TIME)


# 单机鼠标右键
RightClick(x: int, y: int, waitTime: float = OPERATION_WAIT_TIME)


# 按鼠标左键
PressMouse(x: int, y: int, waitTime: float = OPERATION_WAIT_TIME)


# 释放鼠标左键
ReleaseMouse(waitTime: float = OPERATION_WAIT_TIME)


# 按鼠标中键与右键,释放鼠标中键与右键
RightPressMouse(waitTime: float = OPERATION_WAIT_TIME)
RightReleaseMouse(waitTime: float = OPERATION_WAIT_TIME)
MiddlePressMouse(waitTime: float = OPERATION_WAIT_TIME)
MiddleReleaseMouse(waitTime: float = OPERATION_WAIT_TIME)


# 鼠标移动,模拟鼠标从当前光标移动到点 x, y。
# moveSpeed:1 正常速度,< 1 移动速度较慢,> 1 移动速度更快。
MoveTo(x: int, y: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标左键拖放,模拟鼠标左键从点 x1、y1 拖放到点 x2、y2。
# moveSpeed:1 正常速度,< 1 移动速度较慢,> 1 移动速度更快。
DragDrop(x1: int, y1: int, x2: int, y2: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标右键拖放,模拟鼠标右键从点 x1、y1 拖放到点 x2、y2。
# moveSpeed:1 正常速度,< 1 移动速度较慢,> 1 移动速度更快。
RightDragDrop(x1: int, y1: int, x2: int, y2: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标中键拖放
MiddleDragDrop(x1: int, y1: int, x2: int, y2: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标滚轮向下,模拟鼠标滚轮向下。
# wheelTimes: 轮次,默认为1
# interval: 间隔,默认为0.05
# waitTime: 等待时间,默认为0.5
WheelDown(wheelTimes: int = 1, interval: float = 0.05, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标滚轮向上
# wheelTimes: 轮次,默认为1
# interval: 间隔,默认为0.05
# waitTime: 等待时间,默认为0.5
WheelUp(wheelTimes: int = 1, interval: float = 0.05, waitTime: float = OPERATION_WAIT_TIME)


# 鼠标双击
DoubleClick()

2.6、其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# return console window that runs python,打开控制台
automation.GetConsoleWindow()


# 控制台传值(彩色字体),普通传值用WriteLine;
automation.Logger.ColorfulWriteLine('\nI will open <Color=Green>Notepad</Color> and <Color=Yellow>automate</Color> it. Please wait for a while.')


# 显示桌面
automation.ShowDesktop()


# 获取屏幕尺寸
GetScreenSize()


# 确定窗口是否最小化
IsZoomed(handle: int) -> bool


# 桌面是否被锁
IsDesktopLocked() -> bool

3、【实战】RustDesk软件自动安装、指定服务器信息

下面代码引入的文件 的内容

1
2
3
4
5
6
7
8
9
10
192.168.1.78
vEJU4Fasd/asdadagsdaasdgadfasdasfasfaasffffffddddddddddddddda6W8=
rustdesk-1.1.9-putes.exe

---读取顺序---
---第1行 服务器ip---
---第2行 KEY---
---第3行 rustdesk安装包名字[要带拓展名]---

功能:自动安装rustdesk客户端,安装后开启"允许IP直接访问",执行期间不要使用键盘鼠标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# -*- coding: utf-8 -*-
import time
import subprocess
import pyautogui
import pyperclip
import os
import uiautomation as auto


f = open('key.txt', mode='r', encoding='utf-8')
key_list = f.read().split('\n')
f.close()

# 打开软件
subprocess.Popen(key_list[2])
auto.uiautomation.SetGlobalSearchTimeout(5) # 设置全局搜索超时 5

# 获取
print('----开始安装--请勿操作键盘鼠标----')
win_0 = auto.WindowControl(searchDepth=1, Name="", ClassName='H-SMILE-FRAME')
win_0.ButtonControl(Name='同意并安装').Click()
# time.sleep(int(key_list[2]))
time.sleep(10)
for i in range(1, 20):
print(f'{i}')
if os.access(r'C:\Users\Public\Desktop\RustDesk.lnk', os.X_OK):
time.sleep(5)
try:
win_0 = auto.WindowControl(searchDepth=1, Name="", ClassName='H-SMILE-FRAME')
win_0.ButtonControl(Name='关闭').Click()
time.sleep(3)
except:
pass
break
else:
time.sleep(1)

# -------执行-----
print('打开软件')
subprocess.Popen(r"C:\Program Files\RustDesk\RustDesk.exe")
# os.system(r'start C:\"Program Files"\RustDesk\RustDesk.exe')
auto.uiautomation.SetGlobalSearchTimeout(5) # 设置全局搜索超时 5

# 获取
win = auto.WindowControl(searchDepth=1, Name="", ClassName='H-SMILE-FRAME')
print(win.BoundingRectangle)

# 点击最大化
# win.ButtonControl(Name="最大化").Click()

# 控制的应用窗口前置
win.SetTopmost(True)

# 获取 三个点 位置
button = win.TextControl(Name='你的桌面可以通过下面的ID和密码访问。')
# print(button.BoundingRectangle)

# ---把三个点的位置转换成列表,并点击---
a = str(button.BoundingRectangle)

a = list(map(int, a[1:a.find(')')].split(','))) # 把列表中的字符串转成整型

print(a)

# x,y 为 单击坐标,button 为 鼠标左或右键点击('left' / 'right')
pyautogui.click(a[2], a[3] + 20, button='left')
time.sleep(1)
# ---把三个点的位置转换成列表,并点击---END---

# 设置
win.TextControl(Name='允许IP直接访问').Click()

pyautogui.click(a[2], a[3] + 20, button='left')
time.sleep(1)
win.TextControl(Name='ID/中继服务器').Click()
time.sleep(1)


# pyautogui.hotkey('ctrl', 'a') # 按下 ctrl + c 组合键。此次测试不生效
# ---------
def key_cv(c, v):
""" 模拟执行快捷键 """
pyautogui.keyDown(c)
pyautogui.keyDown(v)
pyautogui.keyUp(v)
pyautogui.keyUp(c)


key_cv('ctrl', 'a')

pyperclip.copy(key_list[0]) # 复制
pyperclip.paste() # 粘贴

key_cv('ctrl', 'v')

# ---------

time.sleep(0.5)
pyautogui.press('Tab')
pyautogui.press('Tab')
pyautogui.press('Tab')

# ---------
key_cv('ctrl', 'a')

pyperclip.copy(key_list[1]) # 复制
pyperclip.paste() # 粘贴

key_cv('ctrl', 'v')

time.sleep(1)
pyautogui.press('tab')
pyautogui.press('tab')
time.sleep(0.5)
pyautogui.press('enter')

time.sleep(1)

win.ButtonControl(Name='关闭').Click()
print('--------执行结束--------')
time.sleep(5)

Python 第三方模块之 uiautomation - UI自动化编程
https://flepeng.github.io/021-Python-31-Python-第三方模块-01-windows-相关-Python-第三方模块之-uiautomation-UI自动化编程/
作者
Lepeng
发布于
2021年4月27日
许可协议