自动化测试框架uiautomation使用说明
简介
在常规的模拟鼠标和键盘操作,我们一般使用pyautogui,uiautomation模块不仅能够直接支持这些操作,还能通过控件定位的方式直接定位到目标控件的位置,而不需要自己去获取对应的坐标位置。uiautomation不仅支持任意坐标位置截图,还支持目标控件的截图,缺点在于只能导出后让PIL图像处理库重新读取。其还能获取到ScrollItemPattern对象的控件,通过ScrollIntoView方法进行视图定位,与浏览器的元素定位效果几乎一致。
在常规的热键功能,我们一般使用pynput实现,但是uiautomation的热键注册会比pynput更简单,功能更强,还能直接让你的python程序实现管理员提权
基本原理
项目地址:https://github.com/yinkaisheng/Python-UIAutomation-for-Windows/blob/master/readme_cn.md
uiautomation只支持python3,依赖comtypes和typing这2个包,注意不要使用3.7.6和3.8.1这个2个版本
uiautomation操作程序时会给程序发送WM_GETOBJECT消息,如果程序处理WM_GETOBJECT消息,实现UI Automation Provider,并调用函数UiaReturnRawElementProvider(HWND hwnd,WPARAM wparam,LPARAM lparam,IRawElementProviderSimple *el),此程序就支持UiAutomation。IRawElementProviderSimple就是UI Automation Provider,其包含了各种控件信息,如Name,ClassName,ContorlType,坐标等
基础库源码
import importlib
import json
import sys
import os
import logging
import re
import time
import uiautomation as auto
import subprocess
import shutil
from resource.common_locator import *
from common.common import *
# from lib.Windows import wins
class UIautomation(object):
'''
uiautomation相关操作
'''
element_searchProperties = {}
def __init__(self,report_dir=None,logger=None,**kwargs) -> None:
self.report_dir = report_dir if report_dir else os.getcwd()
self.logger = logger if logger else logging.getLogger(self.__class__.__name__)
self.locator_def = {}
self.root_element = ''
auto.uiautomation.SetGlobalSearchTimeout(15)
@property
def UIA_element(self):
return {
'WindowControl':self.root_element.WindowControl(**self.element_searchProperties),
'EditControl':self.root_element.EditControl(**self.element_searchProperties),
'ButtonControl':self.root_element.ButtonControl(**self.element_searchProperties),
'CustomControl':self.root_element.CustomControl(**self.element_searchProperties),
'TextControl':self.root_element.TextControl(**self.element_searchProperties),
'TableControl':self.root_element.TabControl(**self.element_searchProperties),
'PaneControl':self.root_element.PaneControl(**self.element_searchProperties),
'DataItemControl':self.root_element.DataItemControl(**self.element_searchProperties),
'TabItemControl':self.root_element.TabItemControl(**self.element_searchProperties),
'ComboBoxControl':self.root_element.ComboBoxControl(**self.element_searchProperties),
'ListControl':self.root_element.ListControl(**self.element_searchProperties),
'ListItemControl':self.root_element.ListItemControl(**self.element_searchProperties),
'CheckBoxControl':self.root_element.CheckBoxControl(**self.element_searchProperties),
'MenuBarControl':self.root_element.MenuBarControl(**self.element_searchProperties),
'MenuItemControl':self.root_element.MenuItemControl(**self.element_searchProperties),
'ImageControl':self.root_element.ImageControl(**self.element_searchProperties),
'TreeItemControl':self.root_element.TreeItemControl(**self.element_searchProperties),
'ProgressBarControl':self.root_element.ProgressBarControl(**self.element_searchProperties),
'RadioButtonControl':self.root_element.RadioButtonControl(**self.element_searchProperties),
'GroupControl':self.root_element.GroupControl(**self.element_searchProperties),
'ToolBarControl':self.root_element.ToolBarControl(**self.element_searchProperties),
'SpinnerControl':self.root_element.SpinnerControl(**self.element_searchProperties),
}
def uiautomation_setup(self,common_locator_def={},project_locator_def={}):
'''
1.在common_locator_def的基础上更新locator_def,作为locator的定义查找
'''
self.locator_def = common_locator_def
for app_name,locator_details in project_locator_def.items():
if app_name in self.locator_def:
self.locator_def[app_name].update(locator_details)
else:
self.locator_def[app_name] = locator_details
# self._uiautomation_folder_setup()
def _uiautomation_folder_setup(self):
self.UIAutomation_Timestamp_Folder = time.strftime(
"%Y-%m-%d-%H-%M-%S", time.localtime())
self.UIAUTOMATION_FOLDER = os.path.join(
self.report_dir, "UIAutomation", self.UIAutomation_Timestamp_Folder)
if not os.path.exists(self.UIAUTOMATION_FOLDER):
os.makedirs(self.UIAUTOMATION_FOLDER)
def uiautomation_teardown(self,clear=False):
self._uiautomation_folder_teardown(clear)
def _uiautomation_folder_teardown(self,clear):
if clear and os.path.exists(self.UIAUTOMATION_FOLDER):
shutil.rmtree(self.UIAUTOMATION_FOLDER,ignore_errors=True)
def _create_uiobj_string(self,locator=None,**kwargs):
if locator:
if re.match("\(.*\).*", locator):
string = locator
else:
app_name = locator.split('_')[0]
if app_name in self.locator_def and locator in self.locator_def[app_name]:
string = self.locator_def[app_name][locator]
else:
raise Exception(f'{locator} not defined or cannot be found in resource')
elif kwargs:
kwargs_dict = dict(map(lambda kv:(kv[0].lower(),kv[1]),kwargs.items()))
if 'type' not in kwargs_dict:
raise Exception('must set the type of locator')
string = "({})".format(",".join(["{}={}".format(key, value) for
key, value in kwargs.items()]))
return string
def _create_uiobj_from_string(self,string):
self.root_element = auto
self._update_element_properties(string)
if 'Type' not in self.element_searchProperties:
raise Exception(f'the {string} must have type')
element = self.UIA_element[self.element_searchProperties['Type']]
return element
def _update_element_properties(self,string):
self.element_searchProperties = {}
match_list = re.findall("(?<=\()(.*?)(?=\)$)", string)
int_attr = ['Depth','searchDepth','foundIndex']
for attr_details in match_list:
if attr_details:
for attr_item in attr_details.split(','):
attr_name,attr_value = attr_item.split('=',1)
if attr_name in int_attr:
attr_value = int(attr_value)
self.element_searchProperties[attr_name] = attr_value
def _create_uiobj(self,locator=None,**kwargs):
item_string = self._create_uiobj_string(locator,**kwargs)
print(item_string)
item_string_list = re.split('(?<=\))\.',item_string)
item_name_list = re.findall('(?<=\)\.)(.*?)(?=\()',item_string)
root_item = self._create_uiobj_from_string(item_string_list[0])
if item_name_list:
item_info = dict(zip(item_name_list,item_string_list[1:]))
for item_name,item_attr in item_info.items():
if item_name == 'child':
self._update_element_properties(item_attr)
self.root_element = root_item
element = self.UIA_element[self.element_searchProperties['Type']]
elif item_name == 'next':
element = root_item.GetNextSiblingControl()
elif item_name == 'prev':
element = root_item.GetPreviousSiblingControl()
elif item_name == 'parent':
element = root_item.GetParentControl()
root_item = element
else:
element = root_item
return element
def element_is_exists(self,locator,searchtime=10,**kwargs):
uiobj = self._create_uiobj(locator,**kwargs)
return True if uiobj.Exists(searchtime,1) else False
def check_element_exists(self,locator,**kwargs):
is_exists = self.element_is_exists(locator=locator,**kwargs)
if not is_exists:
raise Exception("element not exit in current page")
def check_element_not_exists(self,locator,**kwargs):
is_exit = self.element_is_exists(locator=locator, **kwargs)
if is_exit:
raise Exception("element exit in current page")
else:
return True
def click_element(self,locator=None,position='left',searchtime=10,**kwargs):
'''
点击element, element的定义根据locator或kwargs。 用法举例:
1. click_element(classname=xx, src=yy, text=zz) # 单层关系kwargs键值对定义方式
2. click_element(locator='(Classname=xx, src=yy, text=zz)') # 单层关系locator定义方式
3. click_element(locator='(ClassName=xx, res=yy).parent().child(text=xx)') # 多层关系只能用locator定义
:param locator: 参照_create_uiobj说明
:param position: 鼠标点击的位置,默认为左键点击,right表示右击,middle表示中间
:param searchtime: 元素定位最大的搜索时间,默认为10s
:param kwargs: 参照_create_uiobj说明
:return: None
'''
uiobj = self._create_uiobj(locator,**kwargs)
if uiobj.Exists(searchtime,1):
if position == 'left':
uiobj.Click()
elif position == 'right':
uiobj.RightClick()
elif position == 'middle':
uiobj.MiddleClick()
else:
raise Exception(f'{locator} click failed')
def doubleclick_element(self,locator=None,searchtime=10,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
if uiobj.Exists(searchtime,1):
uiobj.DoubleClick()
else:
raise Exception(f'{locator} doubleclick failed')
def doubleclick_element_ingore(self,locator=None,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
try:
uiobj.DoubleClick()
except:
logging.info(f"the {locator} doubleclick")
def click_element_ignore(self,locator=None,position='left',**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
try:
if position == 'left':
uiobj.Click()
elif position == 'right':
uiobj.RightClick()
elif position == 'middle':
uiobj.MiddleClick()
except:
pass
def longclick_element(self,locator=None,position='left',presstime=10,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
if position == 'left':
uiobj.PressMouse()
time.sleep(presstime)
uiobj.ReleaseMouse()
elif position == 'right':
uiobj.RightPressMouse()
time.sleep(presstime)
uiobj.RightReleaseMouse()
elif position == 'middle':
uiobj.MiddlePressMouse()
time.sleep(presstime)
uiobj.MiddleReleaseMouse()
def drapdrop_element(self,x,y,locator=None,position='left',movespeeds=2,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
if position == 'left':
uiobj.PressMouse()
auto.MoveTo(x,y,movespeeds)
auto.ReleaseMouse()
elif position == 'right':
uiobj.RightPressMouse()
auto.MoveTo(x,y,movespeeds)
auto.RightReleaseMouse()
def wait_element_appear(self,locator=None,timeout=5,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
if not auto.WaitForExist(uiobj,timeout=timeout):
raise Exception(f'the {uiobj} not exists in {timeout}')
def wait_element_disappear(self, locator=None, timeout=5, **kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
if not auto.WaitForDisappear(uiobj,timeout=timeout):
raise Exception(f'the {uiobj} not disappear in {timeout}')
def click_by_coordinate(self,x,y):
auto.Click(x,y)
def select_element(self,locator=None,select_text=None,**kwargs):
'''
点击locator后选择下拉框select_locator, locator表示哪个元素是可以进行下拉的,select_locator表示具体的下拉框元素
:param locator: 参照_create_uiobj说明
:param select_locator: 下拉框元素
:param select_text: 下拉元素的文本
:param kwargs: 参照_create_uiobj说明
:return: None
'''
select_text = select_text.upper()
combox = self._create_uiobj(locator=locator,**kwargs)
try:
for item,depth in auto.WalkControl(combox,includeTop=True,maxDepth=2):
if item.Name == select_text:
combox.Select(select_text)
break
elif depth != 2 or item.ControlType != auto.ControlType.ListItemControl:
continue
except:
raise Exception(f'the {select_text} not find in {locator}')
def clear_element_text(self,locator=None,**kwargs):
'''
清除文本编辑框中所有内容,locator的type一定为EditControl,且能进行编辑
'''
self.click_element(locator=locator,**kwargs)
auto.SendKeys('{Ctrl}(A)')
auto.SendKey(auto.Keys.VK_CLEAR)
def send_text(self,text=None):
'''
发送文字,符号,英文字母到文本中,locator的type是EditControl,且能进行编辑
'''
# self.click_element(locator=locator,**kwargs)
auto.SendKeys(text=text)
def send_keyboard(self,keyboard):
'''
按键操作,对键盘进行单个按钮操作,无组合
param keyboard:表示具体的键盘按键,例如:ESC,BACK,F1,0,o,CAPITAL等
'''
upperKey = keyboard.upper()
if upperKey in auto.SpecialKeyNames:
auto.SendKey(auto.SpecialKeyNames[upperKey])
elif keyboard in auto.CharacterCodes:
auto.SendKey(auto.CharacterCodes[keyboard])
else:
logging.error(f'SendKey failed the {keyboard} not in auto dict')
def press_keyborad(self,keyboard):
upperKey = keyboard.upper()
if upperKey in auto.SpecialKeyNames:
auto.PressKey(auto.SpecialKeyNames[upperKey])
elif keyboard in auto.CharacterCodes:
auto.PressKey(auto.CharacterCodes[keyboard])
else:
logging.error(f'PressKey failed the {keyboard} not in auto dict')
def release_keyboard(self,keyboard):
upperKey = keyboard.upper()
if upperKey in auto.SpecialKeyNames:
auto.ReleaseKey(auto.SpecialKeyNames[upperKey])
elif keyboard in auto.CharacterCodes:
auto.ReleaseKey(auto.CharacterCodes[keyboard])
else:
logging.error(f'ReleaseKey failed the {keyboard} not in auto dict')
def sendkeys(self,keyboard):
'''
满足各种键盘操作,可发送文字,按键,组合按键等
param keyboard: 可以发送文字,英文,数字,特殊符号等
发送组合按键如下: {Ctrl}(AB) ctrl+A+B
{ALT}(n) ALT+n
{Ctrl}{v 3} 表示同时按下 Ctrl+v,松开 Ctrl+v,然后键入'v'2次
{a 3}{B 5} 表示输入a3次,b5次
{Ctrl}{Shift}{A}
{{}hello{}}{Enter} 输入{hello}并按下Enter
'''
auto.SendKeys(keyboard)
def set_element_value(self,locator=None,value=None,**kwargs):
uiobj = self._create_uiobj(locator=locator,**kwargs)
uiobj.GetValuePattern().SetValue(value)
def get_element_value(self,locator=None,**kwargs):
'''
获取元素value值,如果没有返回None
'''
uiobj = self._create_uiobj(locator=locator,**kwargs)
return uiobj.GetValuePattern().Value if uiobj.GetValuePattern().Value else None
def reset_element_value(self,locator=None,new_value=None,**kwargs):
'''
通过按键操作对可输入框进行重新赋值,会将原输入框内容全部替换掉
1.点击输入框
2.cltr+a选择全部内容
3.重新键入新的内容
4.按Enter确认输入完成
'''
self.clear_element_text(locator=locator,**kwargs)
self.send_text(new_value)
time.sleep(1)
self.send_keyboard('Enter')
def get_element_attr(self,attr=None,locator=None,**kwargs):
'''
获取元素属性值
'''
value = ''
attr = attr.lower()
uiobj = self._create_uiobj(locator=locator,**kwargs)
if self.element_is_exists(locator=locator,**kwargs):
if attr == 'name':
value = uiobj.Name
elif attr == 'value':
value = uiobj.GetValuePattern().value
elif attr == 'depth':
value = uiobj.Depth
elif attr == 'type':
value = uiobj.ControlType
elif attr == 'classname':
value = uiobj.ClassName
elif attr == 'id':
value = uiobj.AutomationId
elif attr == 'isselect':
value = uiobj.GetSelectionItemPattern().IsSelected
return value
def scroll_element_until_first(self,locator=None,**kwargs):
'''
向上滑动直到第一个元素
'''
self.click_element(locator=locator,**kwargs)
old_name = self.get_element_attr(attr='name',locator=locator,**kwargs)
while True:
self.send_keyboard('UP')
element_name = self.get_element_attr(attr='name',locator=locator,**kwargs)
if element_name == old_name:
self.send_keyboard('Enter')
logging.info(f'the {locator} have up to the first')
break
else:
old_name = element_name
def scroll_element_until_last(self,locator=None,**kwargs):
'''
向下滑动直到最后一个元素
'''
self.click_element(locator=locator,**kwargs)
old_name = self.get_element_attr(attr='name',locator=locator,**kwargs)
while True:
self.send_keyboard('DOWN')
element_name = self.get_element_attr(attr='name',locator=locator,**kwargs)
if element_name == old_name:
self.send_keyboard('Enter')
logging.info(f'the {locator} have up to the last')
break
else:
old_name = element_name
def scroll_element(self,locator=None,direction='down',step=1,**kwargs):
'''
切换下拉框操作
param direction:切换方向,默认向下
param step:表示按键次数,即up/down的按键次数,默认为1
'''
self.click_element(locator=locator,**kwargs)
for i in range(step):
self.send_keyboard(direction)
self.send_keyboard('Enter')
def scroll_until_element_appear(self,locator=None,text=None,**kwargs):
'''
来回切换下拉框直到需要的元素出现
param text:表示下拉框元素Name,如果下拉元素不存在,则停留到最上面元素
'''
self.click_element(locator=locator,**kwargs)
current_name = self.get_element_attr('name',locator=locator,**kwargs)
direction = 'down'
change_direction_time = 0
while True:
if current_name == text:
logging.info(f'the {locator} have find {text}')
self.send_keyboard('Enter')
break
else:
old_name = current_name
self.send_keyboard(direction)
current_name = self.get_element_attr('name',locator=locator,**kwargs)
if current_name == old_name:
logger.info(f'the element have {direction} to last')
direction = 'up'
change_direction_time += 1
if change_direction_time >= 2:
raise Exception(f'the {text} no find in {locator}')
def take_screenshot(self):
c = auto.GetRootControl()
rects = auto.GetMonitorsRect()
for rect in rects:
c.CaptureToImage(PICPATH,rect.left,rect.top,rect.width(),rect.height())
return PICPATH
ui = UIautomation(report_dir=REPORT_DIR)
控件控制入门
pip3 install uiautomation
控件分析与可用参数
import subprocess
import uiautomation as auto
subprocess.Popen('notepad.exe')
从桌面的第一层子控件中找到记事本程序的窗口WindowControl
notepadWindow = auto.WindowControl(searchDepth=1, ClassName='Notepad')
print(notepadWindow.Name)
# 设置窗口前置
notepadWindow.SetTopmost(True)
可用参数
● searchFromControl = None 从哪个控件开始找,如果为None,从根控件Desktop开始查找
● searchDepth = 0xFFFFFFFF 搜索深度
● searchInterval = SEARCH_INTERVAL 搜索间隔
● foundIndex = 1 搜索到满足搜索条件的控件索引,从索引1开始
● Name:控件名字
● SubName:控件部分名字
● RegexName:使用re.match匹配符合正则表达式的名字,Name,Subname,RegexName只能使用一个,不能同时使用
● ClassName:类名字
● AutomationId:控件的AuautomationId
● ControlType:控件类型
● Depth:控件相对于searchFromControl的精确深度
● Compare:自定义比较函数function(control:Control,depth:int)->bool
searchDepth和Depth的区别
- searchDepth在指定的深度范围内(1~searchDepth层中的所有子孙控件)搜索第一个满足搜索条件的控件
- Depth只在Depth所在的深度(如果Depth>1,排除1~searchDepth-1层中所有子孙控件)搜索第一个满足搜索条件的控件
具体操作
获取控件信息
安装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顶层窗口)
将鼠标移动到记事本框架后,执行:python automation.py -t0 -c即可以得到编辑器全部子控件的信息
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
先从root查找,然后逐渐往深层次找,不要就直接写,auto.ButtonControl(searchDepth=3,Name='最大化'),能节省查找资源
1.首先从桌面的第一层子控件中找到记事本程序的窗口WindowControl,再从这个窗口查找子控件
notepadWindow = auto.WindowControl(searchDepth=1, ClassName='Notepad')
2.查找notepadWindow所有子孙控件中的第一个EditControl,因为EditControl是第一个子控件,可以不指定深度
edit = notepadWindow.EditControl()
3.先从notepadWindow的第一层子控件中查找TitleBarControl,
4.然后从TitleBarControl的子孙控件中找第二个ButtonControl, 即最大化按钮,并点击按钮
notepadWindow.TitleBarControl(Depth=1).ButtonControl(foundIndex=2).Click()
获取编辑框中的文本
print(edit.GetValuePattern().Value)
输入文本
● 方法1:使用EditControl的ValuePattern():
edit = notepadWindow.EditControl()
edit.GetValuePattern().SetValue('方法1')
● 方法2:直接发送按键指令输入文本
edit.SendKeys('方法2')
● 方法3:复制文本后到剪切板粘贴
auto.SetClipboardText("方法3")
edit.SendKeys('{Ctrl}v')
保存关闭
notepadWindow.TitleBarControl(Depth=1).ButtonControl(foundIndex=3).Click()
确认保存
auto.SendKeys('{ALT}s')
输入文件名,并快捷键点击保存
auto.SendKeys('自动保存{ALT}s')
如果弹出文件名冲突提示,则确认覆盖
auto.SendKeys('{ALT}y')
延迟搜索机制
底层COM对象
Control.Element返回的是UIAutomation底层COM对象IUIAutomationElement,基本上Control的所有属性或者方法都是通过调用IUIAutomationElement COM API和Win32 API实现的
延迟搜索控件
当我们创建一个Control对象时,uiautomation并不会马上开始搜索控件,而是当使用其属性或方法,并且内部的Control.Element是None时,uiautomation才开始搜索控件。如果在uiautomation.TIME_OUT_SECOND(默认为10s)内找不到控件,uiautomation会抛出一个LookupError异常。也可以调用Control.Refind()立马或者重新开始搜索控件。
为了避免函数最终抛出异常,可以使用Control.Exists(maxSearchSeconds,searchIntervalSeconds,printIfNotExists)检查目标控件是否存在
edit = notepadWindow.EditControl()
edit.Exists()
Refind()和Exists()均会使Control.Element无效并重新出发搜索逻辑
还可以使用auto.WaitForExists(control,timeout)等待控件是否在规定时间类出现
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}')#edit.GetValuePattern().SetValue('')
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()
常用功能
导包:import uiautomation as auto
基本方法
常用鼠标动作
OPERATION_WAIT_TIME默认为0.5
● 单击鼠标左键
Click(x: int, y: int, waitTime: float = OPERATION_WAIT_TIME)
● 单击鼠标中键
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)
● 鼠标移动
moveSpeed:1 正常速度,< 1 移动速度较慢,> 1 移动速度更快。
MoveTo(x: int, y: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)
● 鼠标左键拖放
DragDrop(x1: int, y1: int, x2: int, y2: int, moveSpeed: float = 1, waitTime: float = OPERATION_WAIT_TIME)
● 鼠标右键拖放
RightDragDrop(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)
● 鼠标滚轮向上
WheelUp(wheelTimes: int = 1, interval: float = 0.05, waitTime: float = OPERATION_WAIT_TIME)
窗口操作
基本桌面操作
● 获取屏幕尺寸
x,y = auto.GetScreenSize()
● 显示桌面
auto.ShowDesktop()
● 获取uiautomation运行时间
auto.ProcessTime()
● 判断控件是否一致
auto.ControlsAreSame(control1, control2)
获取窗口对象
● 获取桌面对象
c = auto.GetRootControl()
● 返回运行当前python程序的控制台窗口对象
cmdWindow = auto.GetConsoleWindow()#没有就返回None
● 获取当前鼠标位置对应的窗口
ControlFromCursor()返回当前鼠标位置的控件,GetTopLevelControl()获取了该控件对应的顶级窗口对象
c = auto.ControlFromCursor().GetTopLevelControl()
● 获取当前激活窗口对应的对象
c = auto.GetForegroundControl().GetTopLevelControl()
● 获取本地窗口句柄
win = auto.ControlFromCursor().GetTopLevelControl()
handle = win.NativeWindowHandle
● 根据本地窗口句柄获取窗口控制对象
win2 = auto.ControlFromHandle(handle)
auto.ControlsAreSame(win, win2)
#True
● 确定窗口是否最小化
handle:本机窗口的句柄
auto.IsIconic(win.NativeWindowHandle) -> bool
● 确定窗口是否最大化
auto.IsZoomed(win.NativeWindowHandle) -> bool
查找控件方法
● 获取所有子控件
control.GetChildren()
● 获取首个子控件
control.GetFirstChildControl()
● 获取最后一个子控件
control.GetLastChildControl()
● 获取下一个兄弟控件
control.GetNextSiblingControl()
● 获取前一个兄弟控件
control.GetPreviousSiblingControl()
● 获取父控件
control.GetParentControl()
● 获取顶层窗口控件
control.GetTopLevelControl()
● 获取满足条件的祖先控件
func(control:Control,depth:int)->bool,返回True表示找到控件并返回
control.GetAncestorControl(func)
#输入c,d并返回c的类型是auto.WindowControl的控件
control.GetAncestorControl(lambda c, d: isinstance(c, auto.WindowControl))
窗口操作方法
● 隐藏窗口
win.Hide(0)
● 显示窗口
win.Show(0)
● 窗口最大化/最小化
win.Minimize()
win.Maximize()
● 移动窗口到屏幕中心
win.MoveToCenter()
● 窗口置顶
win.SetTopmost(True)
● 获取窗口标题并修改
win.SetWindowText(win.GetWindowText()+"|小小明")
● 获取当前python程序控制台窗口的标题并设置
auto.GetConsoleTitle()
auto.SetConsoleTitle('自定义控制台标题')
遍历子控件
浙公网安备 33010602011771号