You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Sources/pyOpenRPA/Robot/UIDesktop.py

1328 lines
75 KiB

from pywinauto import win32defines, win32structures, win32functions
import pywinauto
import ctypes
import struct
import win32api
import time
from .Utils import ProcessCommunicator
from . import Utils #For ProcessBitness
import re
import copy
############################################
# When import UIDesktop init the other bitness python
# For this type
# UIDesktop.Utils.ProcessBitness.SettingsInit(inSettingsDict)
# inSettingsDict = {
# "Python32FullPath": None, #Set from user: "..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe"
# "Python64FullPath": None, #Set from user
# "Python32ProcessName": "OpenRPAUIDesktopX32.exe", #Config set once
# "Python64ProcessName": "OpenRPAUIDesktopX64.exe" #Config set once
#}
############################################
#logging.basicConfig(filename="Reports\ReportRobotGUIRun_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log", level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
#####Внимание#######
#TODO В перспективе нужно реализовать алгоритм определения разрядности не в Robot.py, а в UIDesktop.py, тк начинают появляться функции, на входе в которые еще неизвестна разрядность элемента + селектор может охватить сразу два элемента из 2-х разных разрядностей - обрабатываться это должно непосредственно при выполнении
####################################
#Info: GUI module of the Robot app (OpenRPA - Robot)
####################################
# GUI Module - interaction with Desktop application
#GUI Naming convention
#<InArgument>_<ActivityName>_<OutArgument - if exist>
#UIO - UI Object (class of pywinauto UI object)
#UIOSelector - List of dict (key attributes)
#PWA - PyWinAuto
#PWASpecification - List of dict (key attributes in pywinauto.find_window notation)
#UIOTree - Recursive Dict of Dict ... (UI Parent -> Child hierarchy)
#UIOInfo - Dict of UIO attributes
#UIOActivity - Activity of the UIO (UI object) from the Pywinauto module
#UIOEI - UI Object info object
#inActivitySpecificationDict:
#{
# ModuleName: <"GUI", str>, - optional
# ActivityName: <Function or procedure name in module, str>,
# ArgumentList: [<Argument 1, any type>, ...] - optional,
# ArgumentDict: {<Argument 1 name, str>:<Argument 1 value, any type>, ...} - optional
#}
#outActivityResultDict:
#{
# ActivitySpecificationDict: {
# ModuleName: <"GUI", str>, -optional
# ActivityName: <Function or procedure name in module, str>,
# ArgumentList: [<Argument 1, any type>, ...] - optional,
# ArgumentDict: {<Argument 1 name, str>: <Argument 1 value, any type>, ...} - optional
# },
# ErrorFlag: <Boolean flag - Activity result has error (true) or not (false), boolean>,
# ErrorMessage: <Error message, str> - required if ErrorFlag is true,
# ErrorTraceback: <Error traceback log, str> - required if ErrorFlag is true,
# Result: <Result, returned from the Activity, int, str, boolean, list, dict> - required if ErrorFlag is false
#}
#inUIOSelector:
#[
# {
# "index":<Позиция элемента в родительском объекте>,
# "depth_start" - глубина, с которой начинается поиск (по умолчанию 1),
# "depth_end" - глубина, до которой ведется поиск (по умолчанию 1),
# "class_name" - наименование класса, который требуется искать,
# "title" - наименование заголовка,
# "rich_text" - наименование rich_text,
# "backend": <"win32"||"uia", only for the 1-st list element> - if not specified, use mDefaultPywinautoBackend
# },
# { ... }
#
#]
#Default parameters
mDefaultPywinautoBackend="win32"
############################
#Новая версия
############################
#Получить список элементов, который удовлетворяет условиям через расширенный движок поиска
#[
# {
# "index":<Позиция элемента в родительском объекте>,
# "depth_start" - глубина, с которой начинается поиск (по умолчанию 1)
# "depth_end" - глубина, до которой ведется поиск (по умолчанию 1)
# "class_name" - наименование класса, который требуется искать
# "title" - наименование заголовка
# "rich_text" - наименование rich_text
# }
#]
################
#return: List of UI Object
#inElement - Входной элемент - показатель, что не требуется выполнять коннект к процессу
#inFlagRaiseException - Флаг True - выкинуть ошибку в случае обнаружении пустого списка
#old name - PywinautoExtElementsGet
def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseException=True):
#Создать копию входного листа, чтобы не менять массив в других верхнеуровневых функциях
inSpecificationList=copy.deepcopy(inSpecificationList)
lResultList=[]
lChildrenList=[]
#Получить родительский объект если на вход ничего не поступило
if inElement is None:
#сформировать спецификацию на получение элемента
lRootElementSpecification=[inSpecificationList[0]]
lRootElementList=PWASpecification_Get_UIO(lRootElementSpecification)
for lRootItem in lRootElementList:
if lRootItem is not None:
lChildrenList.append(lRootItem.wrapper_object())
#Елемент на вход поступил - выполнить его анализ
else:
#Получить список элементов
lElementChildrenList = None
# 2020 06 10 Some times exception ctypes.ArgumentError is apper - set try except with wait
lFlagBool = True
while lFlagBool:
try:
lElementChildrenList = inElement.children()
lFlagBool = False # turn off the loop - all is ok
except ctypes.ArgumentError as e:
time.sleep(30) # error has come - go in sleep and retry
#Поступил index - точное добавление
if 'index' in inSpecificationList[0]:
if inSpecificationList[0]['index']<len(lElementChildrenList):
#Получить дочерний элемент - точное добавление
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['index']])
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['index']))
#Поступил ctrl_index - точное добавление
elif 'ctrl_index' in inSpecificationList[0]:
if inSpecificationList[0]['ctrl_index']<len(lElementChildrenList):
#Получить дочерний элемент
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['ctrl_index']])
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['ctrl_index']))
#Если нет точного обозначения элемента
else:
lFlagGoCheck=True
#Учесть поле depth_start (если указано)
if 'depth_start' in inSpecificationList[0]:
if inSpecificationList[0]["depth_start"]>1:
lFlagGoCheck=False
#Циклический обход по детям, на предмет соответствия всем условиям
for lChildrenItem in lElementChildrenList:
#Обработка глубины depth (рекурсивный вызов для всех детей с занижением индекса глубины)
#По умолчанию значение глубины 1
if 'depth_end' in inSpecificationList[0]:
if inSpecificationList[0]['depth_end']>1:
#Подготовка новой версии спецификации
lChildrenItemNewSpecificationList=inSpecificationList.copy()
lChildrenItemNewSpecificationList[0]=lChildrenItemNewSpecificationList[0].copy()
lChildrenItemNewSpecificationList[0]["depth_end"]=lChildrenItemNewSpecificationList[0]["depth_end"]-1
if 'depth_start' in lChildrenItemNewSpecificationList[0]:
lChildrenItemNewSpecificationList[0]["depth_start"]=lChildrenItemNewSpecificationList[0]["depth_start"]-1
#Циклический вызов для всех детей со скорректированной спецификацией
lResultList.extend(UIOSelector_Get_UIOList(lChildrenItemNewSpecificationList,lChildrenItem,inFlagRaiseException))
#Фильтрация
#TODO Сделать поддержку этих атрибутов для первого уровня селектора
if lFlagGoCheck:
lFlagAddChild=True
#Фильтрация по title
if 'title' in inSpecificationList[0]:
if lChildrenItem.element_info.name != inSpecificationList[0]["title"]:
lFlagAddChild=False
#Фильтрация по title_re (regexp)
if 'title_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["title_re"],lChildrenItem.element_info.name) is None:
lFlagAddChild=False
#Фильтрация по rich_text
if 'rich_text' in inSpecificationList[0]:
if lChildrenItem.element_info.rich_text != inSpecificationList[0]["rich_text"]:
lFlagAddChild=False
#Фильтрация по rich_text_re (regexp)
if 'rich_text_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["rich_text_re"],lChildrenItem.element_info.rich_text) is None:
lFlagAddChild=False
#Фильтрация по class_name
if 'class_name' in inSpecificationList[0]:
if lChildrenItem.element_info.class_name != inSpecificationList[0]["class_name"]:
lFlagAddChild=False
#Фильтрация по class_name_re (regexp)
if 'class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["class_name_re"],lChildrenItem.element_info.class_name) is None:
lFlagAddChild=False
#Фильтрация по friendly_class_name
if 'friendly_class_name' in inSpecificationList[0]:
if lChildrenItem.friendly_class_name() != inSpecificationList[0]["friendly_class_name"]:
lFlagAddChild=False
#Фильтрация по friendly_class_name_re (regexp)
if 'friendly_class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["friendly_class_name_re"],lChildrenItem.friendly_class_name) is None:
lFlagAddChild=False
#Фильтрация по control_type
if 'control_type' in inSpecificationList[0]:
if lChildrenItem.element_info.control_type != inSpecificationList[0]["control_type"]:
lFlagAddChild=False
#Фильтрация по control_type_re (regexp)
if 'control_type_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["control_type_re"],lChildrenItem.element_info.control_type) is None:
lFlagAddChild=False
#Фильтрация по is_enabled (bool)
if 'is_enabled' in inSpecificationList[0]:
if lChildrenItem.is_enabled()!=inSpecificationList[0]["is_enabled"]:
lFlagAddChild=False
#Фильтрация по is_visible (bool)
if 'is_visible' in inSpecificationList[0]:
if lChildrenItem.is_visible()!=inSpecificationList[0]["is_visible"]:
lFlagAddChild=False
#####
#Все проверки пройдены - флаг добавления
if lFlagAddChild:
lChildrenList.append(lChildrenItem)
#Выполнить рекурсивный вызов (уменьшение количества спецификаций), если спецификация больше одного элемента
#????????Зачем в условии ниже is not None ???????????
if len(inSpecificationList)>1 and len(lChildrenList)>0 is not None:
#Вызвать рекурсивно функцию получения следующего объекта, если в спецификации есть следующий объект
for lChildElement in lChildrenList:
lResultList.extend(UIOSelector_Get_UIOList(inSpecificationList[1:],lChildElement,inFlagRaiseException))
else:
lResultList.extend(lChildrenList)
#Условие, если результирующий список пустой и установлен флаг создания ошибки (и inElement is None - не следствие рекурсивного вызова)
if inElement is None and len(lResultList)==0 and inFlagRaiseException:
raise pywinauto.findwindows.ElementNotFoundError("Robot can't find element by the UIOSelector")
return lResultList
#################################################################################################
#Get first (in more than one) UIO (UI Object)
#inSpecificationList - UIOSelector
#inElement - Входной элемент - показатель, что не требуется выполнять коннект к процессу
#inFlagRaiseException - Флаг True - выкинуть ошибку в случае обнаружении пустого списка
#old name - PywinautoExtElementGet
def UIOSelector_Get_UIO (inSpecificationList,inElement=None,inFlagRaiseException=True):
lResult=None
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inSpecificationList,inElement,False)
if len(lResultList)>0:
lResult=lResultList[0]
#Условие, если результирующий список пустой и установлен флаг создания ошибки (и inElement is None - не следствие рекурсивного вызова)
if lResult is None and inFlagRaiseException:
raise pywinauto.findwindows.ElementNotFoundError("Robot can't find element by the UIOSelector")
return lResult
#################################################################################################
#Check if UIO exist (Identified by the UIOSelector)
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#UIOSelector
#old name - -
def UIOSelector_Exist_Bool (inUIOSelector):
lResult=False
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inUIOSelector, None, False)
if len(lResultList)>0:
lResult=True
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_Exist_Bool",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResult = lPIPEResponseDict["Result"]
return lResult
#################################################################################################
#Wait for UIO is appear (at least one of them or all at the same time)
#inSpecificationListList - List of the UIOSelector
#inWaitSecs - Время ожидания объекта в секундах
#inFlagWaitAllInMoment - доп. условие - ожидать появление всех UIOSelector одновременно
#return: [0,1,2] - index of UIOSpecification, which is appear
#old name - -
#####Внимание#####
##Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться)
def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs,inFlagWaitAllInMoment=False):
lResultFlag=False
lSecsSleep = 1 #Настроечный параметр
lSecsDone = 0
lResultList = None
#Цикл проверки
while lResultFlag == False and lSecsDone<inWaitSecs:
#pdb.set_trace()
lResultList=[]
#Итерация проверки
lIndex = 0
for lItem in inSpecificationListList:
lItemResultFlag=UIOSelector_Exist_Bool(lItem)
#Если обнаружен элемент - добавить его индекс в массив
if lItemResultFlag:
lResultList.append(lIndex)
#Инкремент индекса
lIndex=lIndex + 1
#Проверка в зависимости от флага
if inFlagWaitAllInMoment and len(lResultList)==len(inSpecificationListList):
#Условие выполнено
lResultFlag=True
elif not inFlagWaitAllInMoment and len(lResultList)>0:
#Условие выполнено
lResultFlag=True
#Если флаг не изменился - увеличить время и уснуть
if lResultFlag == False:
lSecsDone=lSecsDone+lSecsSleep
time.sleep(lSecsSleep)
return lResultList
#################################################################################################
#Wait for UIO is Disappear (at least one of them or all at the same time)
#inSpecificationListList - List of the UIOSelector
#inWaitSecs - Время ожидания пропажи объекта в секундах
#inFlagWaitAllInMoment - доп. условие - ожидать пропажу всех UIOSelector одновременно
#return: [0,1,2] - index of UIOSpecification, which is Disappear
#old name - -
#####Внимание#####
##Функция ожидания пропажи элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться)
def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs,inFlagWaitAllInMoment=False):
lResultFlag=False
lSecsSleep = 1 #Настроечный параметр
lSecsDone = 0
lResultList = None
#Цикл проверки
while lResultFlag == False and lSecsDone<inWaitSecs:
#pdb.set_trace()
lResultList=[]
#Итерация проверки
lIndex = 0
for lItem in inSpecificationListList:
lItemResultFlag=UIOSelector_Exist_Bool(lItem)
#Если обнаружен элемент - добавить его индекс в массив
if not lItemResultFlag:
lResultList.append(lIndex)
#Инкремент индекса
lIndex=lIndex + 1
#Проверка в зависимости от флага
if inFlagWaitAllInMoment and len(lResultList)==len(inSpecificationListList):
#Условие выполнено
lResultFlag=True
elif not inFlagWaitAllInMoment and len(lResultList)>0:
#Условие выполнено
lResultFlag=True
#Если флаг не изменился - увеличить время и уснуть
if lResultFlag == False:
lSecsDone=lSecsDone+lSecsSleep
time.sleep(lSecsSleep)
return lResultList
#################################################################################################
#Wait for UIO is appear (at least one of them or all at the same time)
#inSpecificationList - UIOSelector
#inWaitSecs - Время ожидания объекта в секундах
#return: Bool - True - UIO is appear
#old name - -
def UIOSelectorSecs_WaitAppear_Bool (inSpecificationList,inWaitSecs):
lWaitAppearList=UIOSelectorsSecs_WaitAppear_List([inSpecificationList],inWaitSecs)
lResult=False
if len(lWaitAppearList)>0:
lResult=True
return lResult
#################################################################################################
#Wait for UIO is disappear (at least one of them or all at the same time)
#inSpecificationList - UIOSelector
#inWaitSecs - Время ожидания пропажи объекта в секундах
#return: Bool - True - UIO is Disappear
#old name - -
def UIOSelectorSecs_WaitDisappear_Bool (inSpecificationList,inWaitSecs):
lWaitDisappearList=UIOSelectorsSecs_WaitDisappear_List([inSpecificationList],inWaitSecs)
lResult=False
if len(lWaitDisappearList)>0:
lResult=True
return lResult
#################################################################################################
#Get process bitness (32 or 64)
#inSpecificationList - UIOSelector
#old name - None
#return None (if Process not found), int 32, or int 64
def UIOSelector_Get_BitnessInt (inSpecificationList):
lResult=None
#Получить объект Application (Для проверки разрядности)
lRootElement=PWASpecification_Get_PWAApplication(inSpecificationList)
if lRootElement is not None:
if lRootElement.is64bit():
lResult=64
else:
lResult=32
return lResult
#################################################################################################
#Get process bitness ("32" or "64")
#inSpecificationList - UIOSelector
#old name - None
#return None (if Process not found), int 32, or int 64
def UIOSelector_Get_BitnessStr (inSpecificationList):
lResult=None
#Получить объект Application (Для проверки разрядности)
lRootElement=PWASpecification_Get_PWAApplication(inSpecificationList)
if lRootElement is not None:
if lRootElement.is64bit():
lResult="64"
else:
lResult="32"
return lResult
#################################################################################################
#Get OS bitness (32 or 64)
#old name - None
#return int 32, or int 64
def Get_OSBitnessInt ():
lResult=32;
if pywinauto.sysinfo.is_x64_OS():
lResult=64;
return lResult;
#################################################################################################
#Safe get other process or None if destination app is the other/same bitness
#inUIOSelector - selector of the destination
#return None or process (of the other bitness)
def UIOSelector_SafeOtherGet_Process(inUIOSelector):
#Default value
lResult = None
#Go check bitness if selector exists
if inUIOSelector:
#Get selector bitness
lUIOSelectorAppBitness = UIOSelector_Get_BitnessStr(inUIOSelector)
if lUIOSelectorAppBitness and Utils.ProcessBitness.mSettingsDict["BitnessProcessCurrent"] != lUIOSelectorAppBitness:
lResult = Utils.ProcessBitness.OtherProcessGet()
return lResult
##################################################################################################
#inControlSpecificationArray - List of dict, dict in pywinauto.find_windows notation
#Backend selection - attribute "backend" ("win32" || "uia") in 1-st list element
#return list of UIO object
#old name - GetControl
def PWASpecification_Get_UIO(inControlSpecificationArray):
#Определение backend
lBackend=mDefaultPywinautoBackend
if "backend" in inControlSpecificationArray[0]:
lBackend=inControlSpecificationArray[0]["backend"]
inControlSpecificationArray[0].pop("backend")
#Подготовка входного массива
inControlSpecificationOriginArray=copy.deepcopy(inControlSpecificationArray)
inControlSpecificationArray=UIOSelector_SearchProcessNormalize_UIOSelector(inControlSpecificationArray)
#Выполнить идентификацию объектов, если передан массив
lResultList=[];
lTempObject=None
if len(inControlSpecificationArray) > 0:
#Сформировать выборку элементов, которые подходят под первый уровень спецификации
# 2020 06 10 Some times exception ctypes.ArgumentError is apper - set try except with wait
lSpecificationLvL1List = None
lFlagBool = True
while lFlagBool:
try:
lSpecificationLvL1List = pywinauto.findwindows.find_elements(**inControlSpecificationArray[0])
lFlagBool = False # turn off the loop - all is ok
except ctypes.ArgumentError as e:
time.sleep(30) # error has come - go in sleep and retry
for lItem in lSpecificationLvL1List:
#Сделать независимую копию и установить информацию о process_id и handle
lItemControlSpecificationArray=copy.deepcopy(inControlSpecificationArray)
lItemControlSpecificationArray[0]["process_id"]=lItem.process_id
lItemControlSpecificationArray[0]["handle"]=lItem.handle
lItemControlSpecificationOriginArray=copy.deepcopy(inControlSpecificationOriginArray)
lItemControlSpecificationOriginArray[0]["process_id"]=lItem.process_id
lItemControlSpecificationOriginArray[0]["handle"]=lItem.handle
#Выполнить подключение к объекту
lRPAApplication = pywinauto.Application(backend=lBackend)
#Проверка разрядности
try:
lRPAApplication.connect(**lItemControlSpecificationArray[0])
except Exception as e:
UIOSelector_TryRestore_Dict(lItemControlSpecificationArray)
try:
lRPAApplication.connect(**lItemControlSpecificationArray[0])
except Exception as e:
lRPAApplication = None
if lRPAApplication is not None:
#lTempObject=lRPAApplication.window(**lItemControlSpecificationArray[0])
#Скорректировано из-за недопонимания структуры
lTempObject=lRPAApplication
#Нормализация массива для целей выборки объекта (удаление конфликтующих ключей)
lItemControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(lItemControlSpecificationOriginArray)
#Циклическое прохождение к недрам объекта
for lWindowSpecification in lItemControlSpecificationArray[0:]:
lTempObject=lTempObject.window(**lWindowSpecification)
#Добавить объект в результирующий массив
lResultList.append(lTempObject)
return lResultList
##################################################################################################
#inControlSpecificationArray - List of dict, dict in pywinauto.find_windows notation
#Backend selection - attribute "backend" ("win32" || "uia") in 1-st list element
#return process application object
#old name - None
def PWASpecification_Get_PWAApplication(inControlSpecificationArray):
inControlSpecificationArray=copy.deepcopy(inControlSpecificationArray)
#Определение backend
lBackend=mDefaultPywinautoBackend
if "backend" in inControlSpecificationArray[0]:
lBackend=inControlSpecificationArray[0]["backend"]
inControlSpecificationArray[0].pop("backend")
#Подготовка входного массива
inControlSpecificationOriginArray=inControlSpecificationArray
inControlSpecificationArray=UIOSelector_SearchProcessNormalize_UIOSelector(inControlSpecificationArray)
#Выполнить идентификацию объектов, если передан массив
lResultList=[];
lTempObject=None
if len(inControlSpecificationArray) > 0:
#Выполнить подключение к объекту
lRPAApplication = pywinauto.Application(backend=lBackend)
#Проверка разрядности
try:
lRPAApplication.connect(**inControlSpecificationArray[0])
except Exception as e:
UIOSelector_TryRestore_Dict(inControlSpecificationArray)
try:
lRPAApplication.connect(**inControlSpecificationArray[0])
except Exception as e:
lRPAApplication = None
if lRPAApplication is not None:
#lTempObject=lRPAApplication.window(**inControlSpecificationArray[0])
#Скорректировано из-за недопонимания структуры
lTempObject=lRPAApplication
return lTempObject
###########################################################################################################
#inElementSpecificationList = UIOSelector (see description on the top of the document)
#result = pywinauto element wrapper instance or None
#old name - AutomationSearchMouseElement
def UIOSelector_SearchChildByMouse_UIO(inElementSpecification):
lGUISearchElementSelected=None
#Настройка - частота обновления подсвечивания
lTimeSleepSeconds=0.4
lElementFoundedList=[]
#Ветка поиска в режиме реального времени
#Сбросить нажатие Ctrl, если оно было
bool(win32api.GetAsyncKeyState(17))
#Оптимизация - получить объект для опроса единажды
lUIORoot=UIOSelector_Get_UIO(inElementSpecification)
lFlagLoop = True
while lFlagLoop:
#Проверить, нажата ли клавиша Ctrl (код 17)
lFlagKeyPressedCtrl=bool(win32api.GetAsyncKeyState(17))
#Подсветить объект, если мышка наведена над тем объектом, который не подсвечивался в прошлый раз
if not lFlagKeyPressedCtrl:
#Получить координаты мыши
(lX,lY) = win32api.GetCursorPos()
lElementFounded={}
#Создать карту пикселей и элементов
#####Внимание! Функция UIOXY_SearchChild_ListDict не написана
lElementFoundedList=UIOXY_SearchChild_ListDict(lUIORoot,lX,lY)
#print(lElementFoundedList)
lElementFounded=lElementFoundedList[-1]["element"]
#Подсветить объект, если он мышь раньше стояла на другом объекте
if lGUISearchElementSelected != lElementFounded:
lGUISearchElementSelected = lElementFounded
#Доработанная функция отрисовки
if lElementFounded is not None:
UIO_Highlight(lElementFounded)
else:
#Была нажата клавиша Ctrl - выйти из цикла
lFlagLoop=False;
#Заснуть до следующего цикла
time.sleep(lTimeSleepSeconds)
#Вернуть результат поиска
return lElementFoundedList
####################################################################################################
#inElementSpecification - UIOSelector
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#old name - AutomationSearchMouseElementHierarchy
def UIOSelector_SearchChildByMouse_UIOTree(inUIOSelector):
lItemInfo = []
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Запустить функцию поиска элемента по мыши
lElementList = UIOSelector_SearchChildByMouse_UIO(inUIOSelector)
lElement = lElementList[-1]['element']
#Detect backend of the elements
lFlagIsBackendWin32 = True
#Если объект имеется (не None), то выполнить построение иерархии
if lElement is not None:
if lElement.backend.name == 'uia':
lFlagIsBackendWin32 = False
#Циклическое создание дерева
#while lElement is not None:
lListIterator=0
lItemInfo2=lItemInfo
for lListItem in lElementList:
lElement = lListItem["element"]
#Продолжать построение иерархии во всех случаях кроме бэк uia & parent() is None
#if not lFlagIsBackendWin32 and lElement.parent() is None:
# lElement = None
#else:
#Получить информацию про объект
lItemInfo2.append(UIOEI_Convert_UIOInfo(lElement.element_info))
#Дообогатить информацией об индексе ребенка в родительском объекте
if "index" in lListItem:
if lListItem["index"] is not None:
lItemInfo2[-1]['ctrl_index']=lListItem["index"]
else:
if "ctrl_index" in lListItem:
lItemInfo2[-1]['ctrl_index']=lListItem["ctrl_index"]
else:
if "ctrl_index" in lListItem:
lItemInfo2[-1]['ctrl_index']=lListItem["ctrl_index"]
#Оборачиваем потомка в массив, потому что у родителя по структуре интерфейса может быть больше одного наследников
lItemInfo2[-1]['SpecificationChild']=[]
lItemInfo2=lItemInfo2[-1]['SpecificationChild']
#Переход на родительский объект
#lElement = lElement.parent()
lListIterator=lListIterator+1
#Добавить информацию о Backend в первый объект
lItemInfo[0]["backend"]=lElement.backend.name
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_SearchChildByMouse_UIOTree",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lItemInfo = lPIPEResponseDict["Result"]
#Вернуть результат
return lItemInfo
####################################################################################################
#inElement- UIO (UI Object)
#old name - PywinautoExtElementCtrlIndexGet
def UIO_GetCtrlIndex_Int(inElement):
lResult = None
#Выполнить алгоритм, если есть Element
if inElement is not None:
lElementParent = inElement.parent()
if lElementParent is not None:
lResult = 0
lFlagFind = True
#Получить список потомков
lElementParentChildrenList = lElementParent.children()
#Циклический поиск до того момента, пока не упремся в текущий элемент
while lFlagFind:
if lResult<len(lElementParentChildrenList):
#Прекратить поиск, если элемент был обнаружен
if inElement == lElementParentChildrenList[lResult]:
lFlagFind = False
else:
#Прекратить поиски, если итератор вышел за пределы списка
if lResult>=len(lElementParentChildrenList):
lResult = None
lFlagFind = False
else:
lResult = lResult + 1
else:
lResult=-1
lFlagFind=False
#Вернуть результат
return lResult
####################################################################################################
# Get the UIO Info list for the selected criteria
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#inSpecificationList - UIOSelector
#old name - PywinautoExtElementsGetInfo
def UIOSelector_Get_UIOInfoList (inUIOSelector, inElement=None):
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inUIOSelector, inElement)
lIterator = 0
for lItem in lResultList:
lResultList[lIterator]=UIOEI_Convert_UIOInfo(lResultList[lIterator].element_info)
lIterator = lIterator + 1
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_Get_UIOInfoList",
"ArgumentList": [inUIOSelector, inElement],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResultList = lPIPEResponseDict["Result"]
return lResultList
####################################################################################################
#Try to restore (maximize) window, if it's was minimized
#(особенность uia backend - он не может прицепиться к окну, если оно свернуто)
#inSpecificationList - UIOSelector
#old name - PywinautoExtTryToRestore
def UIOSelector_TryRestore_Dict(inSpecificationList):
lResult={}
try:
#Подготовка взодного массива
inControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(inSpecificationList)
#Выполнить подключение к объекту. Восстановление необходимо только в бэке win32,
#так как в uia свернутое окно не распознается
lRPAApplication = pywinauto.Application(backend="win32")
lRPAApplication.connect(**inSpecificationList[0])
lRPAApplication.top_window().restore()
except Exception:
True==False
return lResult
####################################################################################################
#Get the list of the UI object activities
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#inControlSpecificationArray - UIOSelector
#old name - ElementActionGetList
def UIOSelector_Get_UIOActivityList (inUIOSelector):
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить объект
lObject=UIOSelector_Get_UIO(inUIOSelector)
lActionList=dir(lObject)
lResult=dir(lObject)
#Выполнить чистку списка от неактуальных методов
for lActionItem in lActionList:
#Удалить те, которые начинаются на _
if lActionItem[0]=='_':
lResult.remove(lActionItem)
#Удалить те, которые начинаются с символа верхнего регистра
if lActionItem[0].isupper():
lResult.remove(lActionItem)
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_Get_UIOActivityList",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResult = lPIPEResponseDict["Result"]
return lResult
####################################################################################################
#Run the activity in UIO (UI Object)
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#inUIOSelector
#inActionName - UIOActivity (name) from Pywinauto
#old name - ElementRunAction
def UIOSelectorUIOActivity_Run_Dict(inUIOSelector, inActionName, inArgumentList=[], inkwArgumentObject={}):
lResult={}
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
#Run activity if SafeOtherProcess is None
if lSafeOtherProcess is None:
#Определить объект
lObject=UIOSelector_Get_UIO(inUIOSelector)
#Получить метод для вызова
lFunction = getattr(lObject, inActionName)
#Выполнить действие
#Обернуто в безопасную обработку, тк для некоторых объектов метод не работает и может выдавать ошибку типа: NotImplementedError: This method not work properly for WinForms DataGrid, use cells()
try:
return lFunction(*inArgumentList,**inkwArgumentObject)
except Exception as e:
#Если ошибка возникла на action get_properties
if inActionName=="get_properties":
lResult={}
#Ручное формирование
lResult["class_name"]=lObject.class_name()
lResult["friendly_class_name"]=lObject.friendly_class_name()
lResult["texts"]=lObject.texts()
lResult["control_id"]=lObject.control_id()
lResult["control_count"]=lObject.control_count()
lResult["automation_id"]=lObject.automation_id()
return lResult
else:
raise e
else:
#Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelectorUIOActivity_Run_Dict",
"ArgumentList": [inUIOSelector, inActionName, inArgumentList, inkwArgumentObject],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResult = lPIPEResponseDict["Result"]
return lResult
####################################################################################################
#Get the UIO dict of the attributes
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#old name - ElementGetInfo
def UIOSelector_Get_UIOInfo(inUIOSelector):
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Подготовка входного массива
inUIOSelector=UIOSelector_SearchUIONormalize_UIOSelector(inUIOSelector)
#Выполнить идентификацию объектов, если передан массив
lResultList=[];
if len(inUIOSelector) > 0:
#Получить объект
lTempObject=UIOSelector_Get_UIO(inUIOSelector)
#Получить инфо объект
lTempObjectInfo = lTempObject.element_info
#Добавить информацию об обнаруженом объекте
lResultList.append(UIOEI_Convert_UIOInfo(lTempObjectInfo))
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_Get_UIOInfo",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResultList = lPIPEResponseDict["Result"]
return lResultList
####################################################################################################
#Search child UIO by the: Parent UIO, X, Y
#inHierarchyList: [{"index":<>,"element":<>}] - technical argument for internal purpose
#result -List of dict [{"index":<>,"element":<>}] -- list of element hierarchy specifications
#old name - GUISearchElementByRootXY
def UIOXY_SearchChild_ListDict(inRootElement,inX,inY,inHierarchyList=[]):
#Инициализация результирующего значения
lResultElement = None
lResultElementX1 = None
lResultElementX2 = None
lResultElementY1 = None
lResultElementY2 = None
lResultHierarchyList=[{'index':None,'element':None}]
#Получить координаты текущего объекта
try:
lRootElementRectX1=inRootElement.element_info.rectangle.left
lRootElementRectX2=inRootElement.element_info.rectangle.right
lRootElementRectY1=inRootElement.element_info.rectangle.top
lRootElementRectY2=inRootElement.element_info.rectangle.bottom
#Добавить объект в результирующий, если координаты попадают в него
if inX>=lRootElementRectX1 and inX<=lRootElementRectX2 and inY>=lRootElementRectY1 and inY<=lRootElementRectY2:
lResultElement = inRootElement
lResultElementX1 = lRootElementRectX1
lResultElementX2 = lRootElementRectX2
lResultElementY1 = lRootElementRectY1
lResultElementY2 = lRootElementRectY2
#Сформировать результирующий обьъект
lParentHierarchy = inHierarchyList
if len(lParentHierarchy)==0:
lParentHierarchy.append({"index":None,"element":lResultElement})
else:
lParentHierarchy[-1]["element"] = lResultElement
lResultHierarchyList=lParentHierarchy
#Получить список детей и добавить в карту
lChildIterator=0
for lChildElement in inRootElement.children():
#Сформировать результирующий массив
lChildFoundedHierarchyList = lParentHierarchy.copy()
lChildFoundedHierarchyList.append({'index': lChildIterator})
lChildFoundedHierarchyList = UIOXY_SearchChild_ListDict(lChildElement,inX,inY, lChildFoundedHierarchyList)
lChildFoundedElement = lChildFoundedHierarchyList[-1]["element"]
#Установить обнаруженный элемент, если текущий результат пустой
if lResultElement is None and lChildFoundedElement is not None:
lResultElement = lChildFoundedElement
lResultElementX1 = lResultElement.element_info.rectangle.left
lResultElementX2 = lResultElement.element_info.rectangle.right
lResultElementY1 = lResultElement.element_info.rectangle.top
lResultElementY2 = lResultElement.element_info.rectangle.bottom
lResultHierarchyList = lChildFoundedHierarchyList
#Выполнить сверку lChildFoundedElement и lResultElement если оба имеются
elif lResultElement is not None and lChildFoundedElement is not None:
#Правила перезатирания карты, если имеется старый объект
#[Накладываемый объект] - НО - ElementNew
#[Имеющийся объект] - ИО - ElementOld
#3 типа вхождения объектов
#тип 1 - [имеющийся объект] полностью входит в [накладываемый объект] (ИО X1 Y1 >= НО X1 Y1; ИО X2 Y2 <= НО X2 Y2) - не вносить НО в bitmap в эти диапазоны
#тип 2 - [имеющийся объект] полностью выходит за пределы [накладываемого объекта] (ИО X1 Y1 < НО X1 Y1; ИО X2 Y2 > НО X2 Y2) - вносить НО в bitmap
#тип 3 - [имеющийся объект] частично входит в [накладываемый объект] (все остальные случаи)- вносить НО в bitmap
#Получить координаты ИО
lChildFoundedElementInfo = lChildFoundedElement.element_info
#lElementNew = inElement
lChildFoundedElementX1 = lChildFoundedElementInfo.rectangle.left
lChildFoundedElementX2 = lChildFoundedElementInfo.rectangle.right
lChildFoundedElementY1 = lChildFoundedElementInfo.rectangle.top
lChildFoundedElementY2 = lChildFoundedElementInfo.rectangle.bottom
#Проверка вхождения по типу 1
if (lResultElementX1>=lChildFoundedElementX1) and (lResultElementY1>=lChildFoundedElementY1) and (lResultElementX2<=lChildFoundedElementX2) and (lResultElementY2<=lChildFoundedElementY2):
False == True
#Проверка вхождения по типу 3
elif (lResultElementX1<lChildFoundedElementX1) and (lResultElementY1<lChildFoundedElementY1) and (lResultElementX2>lChildFoundedElementX2) and (lResultElementY2>lChildFoundedElementY2):
lResultElement = lChildFoundedElement
lResultElementX1 = lChildFoundedElementX1
lResultElementX2 = lChildFoundedElementX2
lResultElementY1 = lChildFoundedElementY1
lResultElementY2 = lChildFoundedElementY2
lResultHierarchyList = lChildFoundedHierarchyList
#Проверка вхождения по типу 2
else:
lResultElement = lChildFoundedElement
lResultElementX1 = lChildFoundedElementX1
lResultElementX2 = lChildFoundedElementX2
lResultElementY1 = lChildFoundedElementY1
lResultElementY2 = lChildFoundedElementY2
lResultHierarchyList = lChildFoundedHierarchyList
lChildIterator=lChildIterator+1
except Exception as e:
False == False
return lResultHierarchyList
###################################################################################################
#Get list of child UIO's by Parent UIOSelector
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#inControlSpecificationArray- UIOSelector
#old name - ElementGetChildElementList
def UIOSelector_GetChildList_UIOList(inUIOSelector=[], inBackend=mDefaultPywinautoBackend):
#mRobotLogger.info(f"File!!!!")
#mRobotLogger.info(f"inSelector:{str(inUIOSelector)}, inBackend:{str(inBackend)}")
#pdb.set_trace()
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Подготовка входного массива
inUIOSelector=UIOSelector_SearchUIONormalize_UIOSelector(inUIOSelector)
#Выполнить идентификацию объектов, если передан массив
lResultList=[]
#ctypes.windll.user32.MessageBoxW(0, str(inControlSpecificationArray), "Your title", 1)
if len(inUIOSelector) > 0:
#Получить объект
lTempObject = UIOSelector_Get_UIO(inUIOSelector)
#Получить список дочерних объектов
lTempChildList = lTempObject.children()
lIterator=0
#Подготовить результирующий объект
for lChild in lTempChildList:
lTempObjectInfo=lChild.element_info
#Добавить информацию об обнаруженом объекте
lObjectInfoItem=UIOEI_Convert_UIOInfo(lTempObjectInfo)
#Итератор внутри объекта (для точной идентификации)
lObjectInfoItem['ctrl_index']=lIterator
lResultList.append(lObjectInfoItem)
#Инкремент счетчика
lIterator=lIterator+1
else:
lResultList=BackendStr_GetTopLevelList_UIOInfo(inBackend)
#Установка бэк-енда на первый элемент
for lItem in lResultList:
lItem["backend"]=inBackend
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_GetChildList_UIOList",
"ArgumentList": [inUIOSelector, inBackend],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
lResultList = lPIPEResponseDict["Result"]
return lResultList
####################################################################################################
#Подготовить массив для обращшения к поиску элемементов
#inControlSpecificationArray - UIOSelector (can be dirty)
#old name 1 - ElementSpecificationArraySearchPrepare
#old name 2 - ElementSpecificationListNormalize
def UIOSelector_SearchUIONormalize_UIOSelector (inControlSpecificationArray):
lResult=[]
#Циклический обход
for lSpecificationItem in inControlSpecificationArray:
lSpecificationItemNew=lSpecificationItem.copy()
#Перебор всех элементов
for lItemKey,lItemValue in lSpecificationItem.items():
#Флаг удаления атрибута
lFlagRemoveAttribute=False
#############################
#Если является вложенным словарем - удалить
if type(lItemValue) is dict:
lFlagRemoveAttribute=True
#Является типом None
if lItemValue is None:
lFlagRemoveAttribute=True
#Проверка допустимого ключевого слова
if (
lItemKey == "class_name" or
lItemKey == "class_name_re" or
lItemKey == "parent" or
lItemKey == "process" or
lItemKey == "title" or
lItemKey == "title_re" or
lItemKey == "top_level_only" or
lItemKey == "visible_only" or
lItemKey == "enabled_only" or
lItemKey == "best_match" or
lItemKey == "handle" or
lItemKey == "ctrl_index" or
lItemKey == "found_index" or
lItemKey == "predicate_func" or
lItemKey == "active_only" or
lItemKey == "control_id" or
lItemKey == "control_type" or
lItemKey == "auto_id" or
lItemKey == "framework_id" or
lItemKey == "backend"):
True == True
else:
lFlagRemoveAttribute=True
#############################
#Конструкция по удалению ключа из словаря
if lFlagRemoveAttribute:
lSpecificationItemNew.pop(lItemKey)
#Проверит наличие ctrl_index - если он есть, то удалить control_id и control_type из-за того, что они мешают друг другу
if 'ctrl_index' in lSpecificationItemNew:
if "control_id" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_id")
if "control_type" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_type")
#Проверить наличие handle - если он есть, то удалить process, control_id и control_type из-за того, что они мешают друг другу
if 'handle' in lSpecificationItemNew:
if "control_id" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_id")
if "control_type" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_type")
if "process" in lSpecificationItemNew:
lSpecificationItemNew.pop("process")
#Иначе Проверить наличие process - если он есть, то удалить тк он нужен только при подключении к процессу
if 'process' in lSpecificationItemNew:
lSpecificationItemNew.pop("process")
#Добавить строку в результирующий массив
lResult.append(lSpecificationItemNew)
#Вернуть результат
return lResult
####################################################################################################
#Подготовить массив для обращшения к поиску процесса (отличается от поиска элемента, тк данная функция нужна для нормализации спецификации для подключения к процессу с окнами)
#inControlSpecificationArray - UIOSelector (can be dirty)
#old name 1 - ElementSpecificationArraySearchPrepare
#old name 2 - ElementSpecificationListNormalize
def UIOSelector_SearchProcessNormalize_UIOSelector (inControlSpecificationArray):
lResult=[]
#Циклический обход
for lSpecificationItem in inControlSpecificationArray:
lSpecificationItemNew=lSpecificationItem.copy()
#Перебор всех элементов
for lItemKey,lItemValue in lSpecificationItem.items():
#Флаг удаления атрибута
lFlagRemoveAttribute=False
#############################
#Если является вложенным словарем - удалить
if type(lItemValue) is dict:
lFlagRemoveAttribute=True
#Является типом None
if lItemValue is None:
lFlagRemoveAttribute=True
#Проверка допустимого ключевого слова
if (
lItemKey == "class_name" or
lItemKey == "class_name_re" or
lItemKey == "parent" or
lItemKey == "process" or
lItemKey == "title" or
lItemKey == "title_re" or
lItemKey == "top_level_only" or
lItemKey == "visible_only" or
lItemKey == "enabled_only" or
lItemKey == "best_match" or
lItemKey == "handle" or
lItemKey == "ctrl_index" or
lItemKey == "found_index" or
lItemKey == "predicate_func" or
lItemKey == "active_only" or
lItemKey == "control_id" or
lItemKey == "control_type" or
lItemKey == "auto_id" or
lItemKey == "framework_id" or
lItemKey == "backend"):
True == True
else:
lFlagRemoveAttribute=True
#############################
#Конструкция по удалению ключа из словаря
if lFlagRemoveAttribute:
lSpecificationItemNew.pop(lItemKey)
#Проверит наличие ctrl_index - если он есть, то удалить control_id и control_type из-за того, что они мешают друг другу
if 'ctrl_index' in lSpecificationItemNew:
if "control_id" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_id")
if "control_type" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_type")
#Проверить наличие handle - если он есть, то удалить process, control_id и control_type из-за того, что они мешают друг другу
if 'handle' in lSpecificationItemNew:
if "control_id" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_id")
if "control_type" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_type")
if "process" in lSpecificationItemNew:
lSpecificationItemNew.pop("process")
#Иначе Проверить наличие process - если он есть, то удалить title, control_id и control_type из-за того, что они мешают друг другу
elif 'process' in lSpecificationItemNew:
if "control_id" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_id")
if "control_type" in lSpecificationItemNew:
lSpecificationItemNew.pop("control_type")
if "title" in lSpecificationItemNew:
lSpecificationItemNew.pop("title")
#Добавить строку в результирующий массив
lResult.append(lSpecificationItemNew)
#Вернуть результат
return lResult
####################################################################################################
#Transfer UI object element info (pywinauto) to UIOInfo (dict of attributes)
#inElementInfo - UIOEI
#old name - ElementInfoExportObject
def UIOEI_Convert_UIOInfo(inElementInfo):
#Подготовить выходную структуру данных
lResult = {"title":None,"rich_text":None,"process_id":None,"process":None,"handle":None,"class_name":None,"control_type":None,"control_id":None,"rectangle":{"left":None,"top":None,"right":None,"bottom":None}, 'runtime_id':None}
#Проверка name
try:
lResult['title']=inElementInfo.name
except Exception as e:
True == False
#Проверка rich_text
try:
lResult['rich_text']=inElementInfo.rich_text
except Exception as e:
True == False
#Проверка process_id
try:
lResult['process_id']=inElementInfo.process_id
lResult['process']=inElementInfo.process_id
except Exception as e:
True == False
#Проверка handle
try:
lResult['handle']=inElementInfo.handle
except Exception as e:
True == False
#Проверка class_name
try:
lResult['class_name']=inElementInfo.class_name
except Exception as e:
True == False
#Проверка control_type
try:
lResult['control_type']=inElementInfo.control_type
except Exception as e:
True == False
#Проверка control_id
try:
if inElementInfo.control_id!=0:
lResult['control_id']=inElementInfo.control_id
except Exception as e:
True == False
#Проверка rectangle left
try:
lResult['rectangle']['left']=inElementInfo.rectangle.left
except Exception as e:
True == False
#Проверка rectangle right
try:
lResult['rectangle']['right']=inElementInfo.rectangle.right
except Exception as e:
True == False
#Проверка rectangle top
try:
lResult['rectangle']['top']=inElementInfo.rectangle.top
except Exception as e:
True == False
#Проверка rectangle bottom
try:
lResult['rectangle']['bottom']=inElementInfo.rectangle.bottom
except Exception as e:
True == False
#Проверка runtime_id
try:
lResult['runtime_id']=inElementInfo.runtime_id
except Exception as e:
True == False
#Вернуть результат
return lResult
###################################################################################################
#Get list of top level
#old name - GetRootElementList
def BackendStr_GetTopLevelList_UIOInfo(inBackend=mDefaultPywinautoBackend):
#Получить список объектов
lResultList=pywinauto.findwindows.find_elements(top_level_only=True,backend=inBackend)
lResultList2=[]
for lI in lResultList:
lTempObjectInfo=lI
lResultList2.append(UIOEI_Convert_UIOInfo(lI));
return lResultList2
###################################################################################################
#Highlight the UI object
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#old name - ElementDrawOutlineNew
def UIOSelector_Highlight(inUIOSelector):
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
UIO_Highlight(UIOSelector_Get_UIO(inUIOSelector))
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_Highlight",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
return lPIPEResponseDict["Result"]
return True
###################################################################################################
#inSpecificationArray - UIOSelector
#!!!!!Safe call is included (you can set activity and UIDesktop will choose the bitness and return the result)!!!!!
#old name - ElementDrawOutlineNewFocus
def UIOSelector_FocusHighlight(inUIOSelector):
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
UIO_FocusHighlight(UIOSelector_Get_UIO(inUIOSelector))
else:
# Run function from other process with help of PIPE
lPIPEResuestDict = {"ModuleName": "UIDesktop", "ActivityName": "UIOSelector_FocusHighlight",
"ArgumentList": [inUIOSelector],
"ArgumentDict": {}}
# Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами
ProcessCommunicator.ProcessChildSendObject(lSafeOtherProcess, lPIPEResuestDict)
# Get answer from child process
lPIPEResponseDict = ProcessCommunicator.ProcessChildReadWaitObject(lSafeOtherProcess)
if lPIPEResponseDict["ErrorFlag"]:
raise Exception(
f"Exception was occured in child process (message): {lPIPEResponseDict['ErrorMessage']}, (traceback): {lPIPEResponseDict['ErrorTraceback']}")
else:
return lPIPEResponseDict["Result"]
return True
###################################################################################################
#old name - draw_outline_new
def UIO_Highlight(lWrapperObject,colour='green',thickness=2,fill=win32defines.BS_NULL,rect=None,inFlagSetFocus=False):
if lWrapperObject is not None:
"""
Draw an outline around the window.
* **colour** can be either an integer or one of 'red', 'green', 'blue'
(default 'green')
* **thickness** thickness of rectangle (default 2)
* **fill** how to fill in the rectangle (default BS_NULL)
* **rect** the coordinates of the rectangle to draw (defaults to
the rectangle of the control)
"""
if inFlagSetFocus:
#Установить фокус на объект, чтобы было видно выделение
lWrapperObject.set_focus()
time.sleep(0.5)
# don't draw if dialog is not visible
#if not lWrapperObject.is_visible():
# return
colours = {
"green": 0x00ff00,
"blue": 0xff0000,
"red": 0x0000ff,
}
# if it's a known colour
if colour in colours:
colour = colours[colour]
if rect is None:
rect = lWrapperObject.rectangle()
# create the pen(outline)
pen_handle = win32functions.CreatePen(
win32defines.PS_SOLID, thickness, colour)
# create the brush (inside)
brush = win32structures.LOGBRUSH()
brush.lbStyle = fill
brush.lbHatch = win32defines.HS_DIAGCROSS
brush_handle = win32functions.CreateBrushIndirect(ctypes.byref(brush))
# get the Device Context
dc = win32functions.CreateDC("DISPLAY", None, None, None )
# push our objects into it
win32functions.SelectObject(dc, brush_handle)
win32functions.SelectObject(dc, pen_handle)
# draw the rectangle to the DC
win32functions.Rectangle(
dc, rect.left, rect.top, rect.right, rect.bottom)
# Delete the brush and pen we created
win32functions.DeleteObject(brush_handle)
win32functions.DeleteObject(pen_handle)
# delete the Display context that we created
win32functions.DeleteDC(dc)
###################################################################################################
#Аналог подсвечивания + установка фокуса
#old name - draw_outline_new_focus
def UIO_FocusHighlight(lWrapperObject,colour='green',thickness=2,fill=win32defines.BS_NULL,rect=None):
UIO_Highlight(lWrapperObject,'green',2,win32defines.BS_NULL,None,True)
#Определить разрядность процесса
lProcessBitnessStr = str(struct.calcsize("P") * 8)