python控制Windows桌面程序自动化模块uiautomation

news2024/9/24 9:21:12

github仓库地址:GitHub - yinkaisheng/Python-UIAutomation-for-Windows: (Donot use 3.7.6,3.8.1):snake:Python 3 wrapper of Microsoft UIAutomation. Support UIAutomation for MFC, WindowsForm, WPF, Modern UI(Metro UI), Qt, IE, Firefox, Chrome ...

 

uiautomation封装了微软UIAutomation API,支持自动化Win32,MFC,WPF,Modern UI(Metro UI), Qt, IE, Firefox(version<=56 or >=60, Firefox57是第一个Rust开发版本,前几个Rust开发版本个人测试发现不支持), Chrome和基于Electron开发的应用程序(Chrome浏览器和Electron应用需要加启动参数--force-renderer-accessibility才能支持UIAutomation).

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

2.0版本之前的代码请参考API changes修改代码。

uiautomation支持在Windows XP SP3或更高版本的Windows桌面系统上运行。

如果是Windows XP系统,请确保系统目录有这个文件:UIAutomationCore.dll。如果没有,需要安装补丁 KB971513 才能支持UIAutomtion.

在Windows 7或更高版本Windows系统上使用uiautomation时,要以管理员权限运行Python, 否则uiautomation运行时很多函数可能会执行失败或抛出异常。 或者先以管理员权限运行cmd.exe,在cmd中再调用Python,如下图中cmd窗口标题中显示了管理员

安装pip install uiautomation后,在Python的Scripts(比如C:\Python37\Scripts)目录中会有一个文件automation.py, 或者使用源码根目录里的automation.py。automation.py是用来枚举控件树结构的一个脚本。

运行'automation.py -h',查看命令帮助,写自动化代码时要根据它的输出结果来写对应的代码。 

理解上图中各个参数的意义并运行下面命令查看程序的执行结果。
automation.py -t 0, 打印当前激活窗口的所有控件
automation.py -r -d 1 -t 0, 打印桌面(树的根控件 )和它的第一层子窗口(TopLevel顶层窗口)

automation.py 显示了控件树中的各个控件(Control)的部分属性和控件支持的Pattern.

根据微软 UIAutomation API,一个具体类型的Control必须支持或选择支持某种Pattern,如下图

参考 Control Pattern Mapping for UI Automation Clients 查看全部的Control-Pattern支持表格。

uiautomation中封装了Windows UIAutomation中的各个Control和Pattern.

Control类型有ButtonControl, TextControl, TreeControl等等。

Pattern类型有ExpandCollapsePattern,InvokePattern等等。

实际使用时,要用Control或Pattern对象来获取控件信息或操作控件。

uiautomation根据你提供的控件属性在控件树中从上往下查找控件。

假设控件树如下:

root(Name='Desktop', Depth=0)
  window1(Depth=1)
    control1-001(Depth=2)
    control1-...(Depth=2)
    ...
    control1-100(Depth=2)
  window2(Name='window2', Depth=1)
    control2-1(Depth=2)
      control2-1-001(Depth=3)
      control2-1-...(Depth=3)
      ...
      control2-1-100(Depth=3)
    control2-2(Depth=2)
    control2-3(Depth=2)
    control2-4(Name='2-4', Depth=2)
      editcontrol(Name='myedit1', Depth=3)
      editcontrol(Name='myedit2', Depth=3)

如果你想找到名字为myedit2的EditControl,并在这个EditControl打字,你可以这样写:

uiautomation.EditControl(searchDepth=3, Name='myedit2').SendKeys('hi')

但是这个代码运行效率并不高,因为控件树中有很多控件,你所查找的EditControl在树的末尾, 从树根部搜索整个控件树需要遍历200多次才能找到这个EditControl, 如果用分层查找并指定查找深度,就可以只查找几次,很快就能找到控件。

代码如下:

window2 = uiautomation.WindowControl(searchDepth=1, Name='window2')#search 2 times
sub = window2.Control(searchDepth=1, Name='2-4')# search 4 times
edit = sub.EditControl(searchDepth=1, Name='myedit2')# search 2 times
edit.SendKeys('hi')

先在root的第一层子控件中查找window2,需要查找2次。 再在window2的第一层子控件中查找control2-4,需要查找4次。 最后在control2-4的第一层子控件中查找myedit2,需要查找2次。 总共需要查找8次就能找到控件。

你还可以 把上面的四行代码合并成一行:

uiautomation.WindowControl(searchDepth=1, Name='window2').Control(searchDepth=1, Name='2-4').EditControl(searchDepth=1, Name='myedit2').SendKeys('hi')

下面来看下操作系统记事本程序的例子.
运行notepad.exe,再运行automation.py -t 3,切换到记事本使记事本成为当前激活窗口, 3秒后automation.py就会把记事本的所有控件打印出来,并保存到日志文件@AutomationLog.txt。

在我的电脑上,输出如下:

ControlType: PaneControl ClassName: #32769 Name: 桌面 Depth: 0 (桌面窗口,树的根控件)
  ControlType: WindowControl ClassName: Notepad Depth: 1 (顶层窗口,记事本窗口)
    ControlType: EditControl ClassName: Edit Depth: 2
      ControlType: ScrollBarControl ClassName: Depth: 3
        ControlType: ButtonControl ClassName: Depth: 4
        ControlType: ButtonControl ClassName: Depth: 4
      ControlType: ThumbControl ClassName: Depth: 3
    ControlType: TitleBarControl ClassName: Depth: 2
      ControlType: MenuBarControl ClassName: Depth: 3
        ControlType: MenuItemControl ClassName: Depth: 4
      ControlType: ButtonControl ClassName: Name: 最小化 Depth: 3
      ControlType: ButtonControl ClassName: Name: 最大化 Depth: 3
      ControlType: ButtonControl ClassName: Name: 关闭 Depth: 3
...

运行如下代码:

# -*- coding: utf-8 -*-
# this script only works with Win32 notepad.exe
# if you notepad.exe is the Windows Store version in Windows 11, you need to uninstall it.
import subprocess
import uiautomation as auto

def test():
    print(auto.GetRootControl())
    subprocess.Popen('notepad.exe', shell=True)
    # 首先从桌面的第一层子控件中找到记事本程序的窗口WindowControl,再从这个窗口查找子控件
    notepadWindow = auto.WindowControl(searchDepth=1, ClassName='Notepad')
    print(notepadWindow.Name)
    notepadWindow.SetTopmost(True)
    # 查找notepadWindow所有子孙控件中的第一个EditControl,因为EditControl是第一个子控件,可以不指定深度
    edit = notepadWindow.EditControl()
    try:
        # 获取EditControl支持的ValuePattern,并用Pattern设置控件文本为"Hello"
        edit.GetValuePattern().SetValue('Hello')# or edit.GetPattern(auto.PatternId.ValuePattern)
    except auto.comtypes.COMError as ex:
        # 如果遇到COMError, 一般是没有以管理员权限运行Python, 或者这个控件没有实现pattern的方法(如果是这种情况,基本没有解决方法)
        # 大多数情况不需要捕捉COMError,如果遇到了就加到try block
        pass
    edit.SendKeys('{Ctrl}{End}{Enter}World')# 在文本末尾打字
    print('current text:', edit.GetValuePattern().Value)# 获取当前文本
    # 先从notepadWindow的第一层子控件中查找TitleBarControl, 
    # 然后从TitleBarControl的子孙控件中找第二个ButtonControl, 即最大化按钮,并点击按钮
    notepadWindow.TitleBarControl(Depth=1).ButtonControl(foundIndex=2).Click()
    # 从notepadWindow前两层子孙控件中查找Name为'关闭'的按钮并点击按钮
    notepadWindow.ButtonControl(searchDepth=2, Name='关闭').Click()
    # 这时记事本弹出是否保存提示,按热键Alt+N不保存退出。
    auto.SendKeys('{Alt}n')

if __name__ == '__main__':
    test()

auto.GetRootControl()返回控件树的根节点(即桌面窗口Desktop)
auto.WindowControl(searchDepth=1, ClassName='Notepad') 创建了一个WindowControl对象, 括号中的参数指定按照什么条件或控件属性在控件树中查找此控件。

控件的__init__函数中,有下列参数可以使用:
searchFromControl = None, 从哪个控件开始查找,如果为None,从根节点Desktop开始查找
searchDepth = 0xFFFFFFFF, 搜索深度
searchInterval = SEARCH_INTERVAL, 搜索间隔
foundIndex = 1 ,搜索到的满足搜索条件的控件索引,索引从1开始
Name 控件名字
SubName 控件部分名字
RegexName 使用re.match匹配符合正则表达式的名字,Name,SubName,RegexName只能使用一个,不能同时使用
ClassName 类名字
AutomationId 控件AutomationId
ControlType 控件类型
Depth 控件相对于searchFromControl的精确深度
Compare 自定义比较函数function(control: Control, depth: int)->bool

searchDepth和Depth的区别是:
searchDepth在指定的深度范围内(包括1~searchDepth层中的所有子孙控件)搜索第一个满足搜索条件的控件
Depth只在Depth所在的深度(如果Depth>1,排除1~searchDepth-1层中的所有子孙控件)搜索第一个满足搜索条件的控件

Control.Element返回IUIAutomation底层COM对象IUIAutomationElement, 基本上Control的所有属性或方法都是通过调用IUIAutomationElement COM API和Win32 API实现的。 当你使用一个Control的属性或方法时,属性或方法内部调用Control.Element并且Control.Element是None时uiautomation才开始搜索控件。 如果在uiautomation.TIME_OUT_SECOND(默认为10)秒内找不到控件,uiautomation就会抛出一个LookupError异常。 搜索到控件后,Control.Element将会有个有效值。 你可以调用Control.Exists(maxSearchSeconds, searchIntervalSeconds)来检查一个控件是否存在,此函数不会抛出异常。 另外可以调用Control.Refind或Control.Exists使Control.Element无效并触发重新搜索逻辑。

例子:

#!python3
# -*- coding:utf-8 -*-
# this script only works with Win32 notepad.exe
# if you notepad.exe is the Windows Store version in Windows 11, you need to uninstall it.
import subprocess
import uiautomation as auto
auto.uiautomation.SetGlobalSearchTimeout(15)  # 设置全局搜索超时 15


def main():
    subprocess.Popen('notepad.exe', shell=True)
    window = auto.WindowControl(searchDepth=1, ClassName='Notepad')
    # 或者使用Compare自定义搜索条件
    # window = auto.WindowControl(searchDepth=1, ClassName='Notepad', Compare=lambda control,depth:control.ProcessId==100)
    edit = window.EditControl()
    # 当第一次调用SendKeys时, uiautomation开始在15秒内搜索控件window和edit
    # 因为SendKeys内部会间接调用Control.Element并且Control.Element值是None
    # 如果在15秒内找不到window和edit,会抛出LookupError异常
    try:
        edit.SendKeys('first notepad')
    except LookupError as ex:
        print("The first notepad doesn't exist in 15 seconds")
        return
    # 第二次调用SendKeys不会触发搜索, 之前的调用保证Control.Element有效
    edit.SendKeys('{Ctrl}a{Del}')
    window.GetWindowPattern().Close()  # 关闭第一个Notepad, window和edit的Element虽然有值,但是无效了

    subprocess.Popen('notepad.exe')  # 运行第二个Notepad
    window.Refind()  # 必须重新搜索
    edit.Refind()  # 必须重新搜索
    edit.SendKeys('second notepad')
    edit.SendKeys('{Ctrl}a{Del}')
    window.GetWindowPattern().Close()  # 关闭第二个Notepad, window和edit的Element虽然有值,但是再次无效了

    subprocess.Popen('notepad.exe')  # 运行第三个Notepad
    if window.Exists(3, 1): # 触发重新搜索
        if edit.Exists(3):  # 触发重新搜索
            edit.SendKeys('third notepad')  # 之前的Exists保证edit.Element有效
            edit.SendKeys('{Ctrl}a{Del}')
        window.GetWindowPattern().Close()
    else:
        print("The third notepad doesn't exist in 3 seconds")


if __name__ == '__main__':
    main()
    

另外可以设置DEBUG_SEARCH_TIME查看搜索控件所遍历的控件数和搜索时间。

import uiautomation as auto
auto.uiautomation.DEBUG_SEARCH_TIME = True

参考demos/automation_calculator.py

目录 demos 中提供了一些例子,请根据这些例子学习使用uiautomation.


如果你发现automation.py不能打印你所看到的程序的控件,这并不是uiautomation的bug, 是因为这个程序是使用DirectUI或自定义控件实现的,不是用微软提供的标准控件实现的, 这个软件必须实现UI Automation Provider才能支持UIAutomation。 微软提供的标准控件默认支持UIAutomation。

比如Chrome浏览器,默认你只能看到最外层的PaneControl Chrome_WidgetWin_1,看不到Chrome具体的子控件, 如果加了参数--force-renderer-accessibility运行Chrome浏览器,就能看到Chrome的子控件了。 这是因为Chrome实现了UI Automation Provider,并做了参数开关 。如果一个软件是用DirectUI实现的,但没有实现UI Automation Provider,那么这个软件是不支持UIAutomation的。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1111389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OJ比赛日历】快周末了,不来一场比赛吗? #10.21-10.27 #11场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-10-21&#xff08;周六&#xff09; #2场比赛2023-10-22…

硬件成本节省60%,四川华迪基于OceanBase的健康大数据数仓建设实践

导语&#xff1a;本文为四川华迪数据计算平台使用 OceanBase 替代 Hadoop 的实践&#xff0c;验证了 OceanBase 在性能和存储成本方面的优势&#xff1a;节省了 60% 的硬件成本&#xff0c;并将运维工作大幅减少&#xff0c;从 Hadoop 海量组件中释放出来&#xff1b;一套系统处…

【Python】文件操作

一、文件的编码 思考:计算机只能识别:0和1&#xff0c;那么我们丰富的文本文件是如何被计算机识别&#xff0c;并存储在硬盘中呢? 答案:使用编码技术( 密码本)将内容翻译成0和1存入 编码技术即:翻译的规则&#xff0c;记录了如何将内容翻译成二进制&#xff0c;以及如何将二…

2022年下半年 软件设计师 上午试卷(41题—75题)

UML活动图用于建模 &#xff08;41&#xff09; 。以下活动图中&#xff0c;活动A1之后&#xff0c;可能的活动执行序列顺序是 &#xff08;42&#xff09; 。 &#xff08;41&#xff09; A. 系统在它的周边环境的语境中所提供的外部可见服务 B. 某一时刻一组对象以及它们之间…

【代码随想录】算法训练营 第七天 第三章 哈希表 Part 2

454. 四数相加 题目 思路 这道题相当于是两数相加的加强版&#xff0c;其实大体思路是一致的&#xff0c;只不过这道题里先把四个数组中的数两两相加&#xff0c;把和作为map的key值&#xff0c;把和出现的次数作为value&#xff0c;这样先遍历完前两个数组&#xff0c;后面再…

nginx平滑升级添加echo模块、localtion配置、rewrite配置

nginx平滑升级添加echo模块、location配置、rewrite配置 文章目录 nginx平滑升级添加echo模块、location配置、rewrite配置1.环境说明&#xff1a;2.nginx平滑升级原理&#xff1a;3.平滑升级nginx&#xff0c;并添加echo模块3.1.查看当前nginx版本以及老版本编译参数信息3.2.下…

【LeetCode-数组】--搜索插入位置

搜索插入位置 class Solution {public int searchInsert(int[] nums, int target) {int n nums.length;int left 0,right n-1;while(left < right){int mid (left right) / 2;if(nums[mid] target){return mid;}else if(nums[mid] > target){right mid - 1;}else…

【一:实战开发testng的介绍】

目录 1、主要内容1.1、为啥要做接口测试1.2、接口自动化测试落地过程1.3、接口测试范围1.4、手工接口常用的工具1.5、自动化框架的设计 2、testng自动化测试框架基本测试1、基本注解2、忽略测试3、依赖测试4、超时测试5、异常测试6、通过xml文件参数测试7、通过data实现数据驱动…

边写代码边学习之mlflow

1. 简介 MLflow 是一个多功能、可扩展的开源平台&#xff0c;用于管理整个机器学习生命周期的工作流程和工件。 它与许多流行的 ML 库内置集成&#xff0c;但可以与任何库、算法或部署工具一起使用。 它被设计为可扩展的&#xff0c;因此您可以编写插件来支持新的工作流程、库和…

Go学习第二章——变量与数据类型

Go变量与数据类型 1 变量1.1 变量概念1.2 变量的使用步骤1.3 变量的注意事项1.4 ""的使用 2 数据类型介绍3 整数类型3.1 有符号整数类型3.2 无符号整数类型3.3 其他整数类型3.4 整型的使用细节 4 小数类型/浮点型4.1 浮点型的分类4.2 简单使用 5 字符类型5.1 字符类型…

【LeetCode】 412. Fizz Buzz

题目链接 文章目录 Python3 【O(n) O(1)】C.emplace_back() 【C 11 之后】 Python3 【O(n) O(1)】 初始版本 class Solution:def fizzBuzz(self, n: int) -> List[str]:ans []for i in range(1, n1):if i % 5 0 and i % 3 0:ans.append("FizzBuzz")elif i % …

【三:Mock服务的使用】

目录 1、工具包2、mock的demo1、get请求2、post请求3、带cookies的请求4、带请求头的请求5、请求重定向 1、工具包 1、&#xff1a;服务包的下载 moco-runner-0.11.0-standalone.jar 下载 2、&#xff1a;运行命令java -jar ./moco-runner-0.11.0-standalone.jar http -p 888…

【Qt控件之微调框、进度条】QSpinBox、QDoubleSpinBox、QDial、QProgressBar介绍及使用

概述 QSpinBox类提供了一个微调框小部件。 QSpinBox适用于处理整数和离散的值集&#xff08;例如&#xff0c;月份名称&#xff09;&#xff1b;对于浮点数值&#xff0c;请使用QDoubleSpinBox。 QSpinBox允许用户通过点击上下按钮或按键盘上的上下箭头来增加/减少当前显示的值…

【交互式分割】——数据可视化

ritm, 交互式分割 数据可视化 数据包括一张图片 正样本点 负样本点 二分类的mask标签 如何模拟多次点击的迭代过程&#xff1f;

ubuntu18.04 RTX3060 rangnet++训练

代码链接&#xff1a; https://github.com/PRBonn/lidar-bonnetal 安装anaconda环境为 CUDA 11.0&#xff08;11.1也可以&#xff09; anaconda环境如下 numpy1.17.2 torchvision0.2.2 matplotlib2.2.3 tensorflow1.13.1 scipy0.19.1 pytorch1.7.1 vispy0.5.3 opencv_python…

【Qt控件之QListWidget】介绍及使用,利用QListWidget、QToolButton、和布局控件实现抽屉式组合控件

概述 QListWidget类提供了基于项目的列表小部件。 QListWidget是一个方便的类&#xff0c;类似于QListView提供的列表视图&#xff0c;但使用经典的基于项目的接口来添加和删除项目。QListWidget使用内部模型来管理列表中的每个QListWidgetItem。 对于更灵活的列表视图小部件…

DVWA-impossible代码审计

文章目录 DVWA靶场—impossible代码审计1.暴力破解&#xff08;Brute Force&#xff09;1.1 代码审计1.2 总结 2.命令注入&#xff08;Command Injection&#xff09;2.1 代码审计2.2 总结 3.跨站请求伪造&#xff08;CSRF&#xff09;3.1 代码审计3.2 总结 4.文件包含漏洞&…

数据挖掘原理与算法

一、什么是闭合项集? Close算法对Apriori算法的改进在什么地方? 闭合项集&#xff1a;就是指一个项集x&#xff0c;它的直接超集的支持度计数都不等于它本身的支持度计数。 改进的地方&#xff1a; 改进方向&#xff1a; 加速频繁项目集合的生成&#xff0c;减少数据库库的扫…

数字秒表VHDL实验箱精度毫秒可回看,视频/代码

名称&#xff1a;数字秒表VHDL精度毫秒可回看 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 数字秒表的VHDL设计&#xff0c;可以显示秒和毫秒。可以启动、停止、复位。要求可以存储6组时间&#xff0c;可以回看存储的时间 本资源内含2个工程文件&am…

Systemverilog断言介绍(二)

3.2 IMMEDIATE ASSERTIONS 即时断言是最简单的断言语句类型。它们通常被认为是SystemVerilog过程代码的一部分&#xff0c;并在代码评估期间访问时进行评估。它们没有时钟或复位的概念&#xff08;除非有时钟/复位控制其封闭的过程块&#xff09;&#xff0c;因此无法验证跨越时…