|
|
from pywinauto import win32defines, win32structures, win32functions
|
|
|
import pdb
|
|
|
import pywinauto
|
|
|
import json
|
|
|
import sys
|
|
|
import ctypes
|
|
|
import struct
|
|
|
import os
|
|
|
import select
|
|
|
import zlib
|
|
|
import win32api
|
|
|
import win32clipboard
|
|
|
import time
|
|
|
import traceback
|
|
|
from . import ProcessCommunicator
|
|
|
from . import JSONNormalize
|
|
|
from threading import Timer
|
|
|
import datetime
|
|
|
import logging
|
|
|
import re
|
|
|
import copy
|
|
|
#Создать файл логирования
|
|
|
# add filemode="w" to overwrite
|
|
|
if not os.path.exists("Reports"):
|
|
|
os.makedirs("Reports")
|
|
|
##########################
|
|
|
#Подготовка логгера Robot
|
|
|
#########################
|
|
|
mRobotLogger=logging.getLogger("RobotLogger")
|
|
|
mRobotLogger.setLevel(logging.INFO)
|
|
|
# create the logging file handler
|
|
|
mRobotLoggerFH = logging.FileHandler("Reports\ReportRobotGUIRun_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log")
|
|
|
mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
|
mRobotLoggerFH.setFormatter(mRobotLoggerFormatter)
|
|
|
# add handler to logger object
|
|
|
mRobotLogger.addHandler(mRobotLoggerFH)
|
|
|
|
|
|
|
|
|
#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, а в GUI.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=inElement.children()
|
|
|
#Поступил 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)
|
|
|
#inSpecificationList - UIOSelector
|
|
|
#old name - -
|
|
|
def UIOSelector_Exist_Bool (inSpecificationList):
|
|
|
lResult=False
|
|
|
#Получить родительский объект если на вход ничего не поступило
|
|
|
lResultList=UIOSelector_Get_UIOList(inSpecificationList,None,False)
|
|
|
if len(lResultList)>0:
|
|
|
lResult=True
|
|
|
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 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;
|
|
|
##################################################################################################
|
|
|
#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:
|
|
|
#Сформировать выборку элементов, которые подходят под первый уровень спецификации
|
|
|
lSpecificationLvL1List = pywinauto.findwindows.find_elements(**inControlSpecificationArray[0])
|
|
|
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):
|
|
|
#Определение 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
|
|
|
#old name - AutomationSearchMouseElementHierarchy
|
|
|
def UIOSelector_SearchChildByMouse_UIOTree(inElementSpecification):
|
|
|
lItemInfo = []
|
|
|
#Запустить функцию поиска элемента по мыши
|
|
|
lElementList = UIOSelector_SearchChildByMouse_UIO(inElementSpecification)
|
|
|
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
|
|
|
#Вернуть результат
|
|
|
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
|
|
|
|
|
|
####################################################################################################
|
|
|
#Получить список информационных объектов, который удовлетворяет условиям
|
|
|
#inSpecificationList - UIOSelector
|
|
|
#old name - PywinautoExtElementsGetInfo
|
|
|
def UIOSelector_Get_UIOInfoList (inSpecificationList,inElement=None):
|
|
|
#Получить родительский объект если на вход ничего не поступило
|
|
|
lResultList=UIOSelector_Get_UIOList(inSpecificationList,inElement)
|
|
|
lIterator = 0
|
|
|
for lItem in lResultList:
|
|
|
lResultList[lIterator]=UIOEI_Convert_UIOInfo(lResultList[lIterator].element_info)
|
|
|
lIterator = lIterator + 1
|
|
|
return lResultList
|
|
|
|
|
|
####################################################################################################
|
|
|
#Check is the UIO/UIO's by the UIOSelector exist
|
|
|
#inSpecificationList - UIOSelector
|
|
|
#old name - PywinautoExtElementExist
|
|
|
def UIOSelector_IsExist_Bool (inSpecificationList):
|
|
|
return len(UIOSelector_Get_UIOList(inSpecificationList))>0
|
|
|
|
|
|
####################################################################################################
|
|
|
#Wait for the UIO by the UIOSelector appear
|
|
|
#inSpecificationList - UIOSelector
|
|
|
#result - { }
|
|
|
#old name - PywinautoExtElementWaitAppear
|
|
|
#############
|
|
|
#Внимание! Старая функция (на замену ей пришла UIOSelectorSecs_WaitAppear_Bool)
|
|
|
#############
|
|
|
def UIOSelector_WaitAppear_Dict(inSpecificationList,inTimeout=60):
|
|
|
lTimeoutSeconds = 0
|
|
|
while (not UIOSelector_IsExist_Bool(inSpecificationList) and inTimeout>lTimeoutSeconds):
|
|
|
lTimeoutSeconds = lTimeoutSeconds + 0.5
|
|
|
#Заснуть на полсекунды
|
|
|
time.sleep(0.5)
|
|
|
return UIOSelector_IsExist_Bool(inSpecificationList)
|
|
|
|
|
|
####################################################################################################
|
|
|
#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
|
|
|
#inControlSpecificationArray - UIOSelector
|
|
|
#old name - ElementActionGetList
|
|
|
def UIOSelector_Get_UIOActivityList (inControlSpecificationArray):
|
|
|
#Получить объект
|
|
|
lObject=UIOSelector_Get_UIO(inControlSpecificationArray)
|
|
|
lActionList=dir(lObject)
|
|
|
lResult=dir(lObject)
|
|
|
#Выполнить чистку списка от неактуальных методов
|
|
|
for lActionItem in lActionList:
|
|
|
#Удалить те, которые начинаются на _
|
|
|
if lActionItem[0]=='_':
|
|
|
lResult.remove(lActionItem)
|
|
|
#Удалить те, которые начинаются с символа верхнего регистра
|
|
|
if lActionItem[0].isupper():
|
|
|
lResult.remove(lActionItem)
|
|
|
return lResult
|
|
|
|
|
|
####################################################################################################
|
|
|
#Run the activity in UIO (UI Object)
|
|
|
#inControlSpecificationArray - UIOSelector
|
|
|
#inActionName - UIOActivity (name) from Pywinauto
|
|
|
#old name - ElementRunAction
|
|
|
def UIOSelectorUIOActivity_Run_Dict(inControlSpecificationArray,inActionName,inArgumentList=[],inkwArgumentObject={}):
|
|
|
lResult={}
|
|
|
#Определить объект
|
|
|
lObject=UIOSelector_Get_UIO(inControlSpecificationArray)
|
|
|
#Получить метод для вызова
|
|
|
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
|
|
|
return lResult
|
|
|
|
|
|
####################################################################################################
|
|
|
#Get the UIO dict of the attributes
|
|
|
#old name - ElementGetInfo
|
|
|
def UIOSelector_Get_UIOInfo(inControlSpecificationArray):
|
|
|
#Подготовка входного массива
|
|
|
inControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(inControlSpecificationArray)
|
|
|
#Выполнить идентификацию объектов, если передан массив
|
|
|
lResultList=[];
|
|
|
if len(inControlSpecificationArray) > 0:
|
|
|
#Получить объект
|
|
|
lTempObject=UIOSelector_Get_UIO(inControlSpecificationArray)
|
|
|
#Получить инфо объект
|
|
|
lTempObjectInfo = lTempObject.element_info
|
|
|
#Добавить информацию об обнаруженом объекте
|
|
|
lResultList.append(UIOEI_Convert_UIOInfo(lTempObjectInfo));
|
|
|
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
|
|
|
#inControlSpecificationArray- UIOSelector
|
|
|
#old name - ElementGetChildElementList
|
|
|
def UIOSelector_GetChildList_UIOList(inControlSpecificationArray=[],inBackend=mDefaultPywinautoBackend):
|
|
|
#Подготовка входного массива
|
|
|
inControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(inControlSpecificationArray)
|
|
|
#Выполнить идентификацию объектов, если передан массив
|
|
|
lResultList=[];
|
|
|
#ctypes.windll.user32.MessageBoxW(0, str(inControlSpecificationArray), "Your title", 1)
|
|
|
if len(inControlSpecificationArray) > 0:
|
|
|
#Получить объект
|
|
|
lTempObject = UIOSelector_Get_UIO(inControlSpecificationArray)
|
|
|
#Получить список дочерних объектов
|
|
|
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
|
|
|
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
|
|
|
#old name - ElementDrawOutlineNew
|
|
|
def UIOSelector_Highlight(inSpecificationArray):
|
|
|
UIO_Highlight(UIOSelector_Get_UIO(inSpecificationArray))
|
|
|
return
|
|
|
|
|
|
###################################################################################################
|
|
|
#inSpecificationArray - UIOSelector
|
|
|
#old name - ElementDrawOutlineNewFocus
|
|
|
def UIOSelector_FocusHighlight(inSpecificationArray):
|
|
|
UIO_FocusHighlight(UIOSelector_Get_UIO(inSpecificationArray))
|
|
|
return
|
|
|
|
|
|
###################################################################################################
|
|
|
#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)
|
|
|
############################
|
|
|
#Старая версия
|
|
|
# Определять флаг Debug, если как второй входной параметр не поступил ключ RELEASE
|
|
|
############################
|
|
|
mFlagIsDebug=True
|
|
|
if (len(sys.argv)>=2):
|
|
|
if (sys.argv[1].upper()=="RELEASE"):
|
|
|
mFlagIsDebug=False
|
|
|
#Оповещение о выбранном режиме
|
|
|
if mFlagIsDebug:
|
|
|
mRobotLogger.info("Robot/GUI: Debug mode, x"+lProcessBitnessStr)
|
|
|
print ("Robot/GUI: Debug mode, x"+lProcessBitnessStr)
|
|
|
else:
|
|
|
mRobotLogger.info("Robot/GUI: Release mode, x"+lProcessBitnessStr)
|
|
|
#Нельзя делать print в release mode тк print делает вывод в PIPE поток, что нарушает последовательность взаимодействия с родительским процессом
|
|
|
#print ("Robot/GUI: Release mode, x"+lProcessBitnessStr)
|
|
|
#for line in sys.stdin:
|
|
|
# lText=lText+line;
|
|
|
#ctypes.windll.user32.MessageBoxW(0, lText, "Your title", 1)
|
|
|
|
|
|
buffer = ""
|
|
|
lJSONInputString=""
|
|
|
|
|
|
#Выполнить чтение буфера, если не отладка библиотеки
|
|
|
if not mFlagIsDebug:
|
|
|
while True:
|
|
|
lProcessResponse={"ErrorFlag":False}
|
|
|
try:
|
|
|
#Ожидаем синхронно поступление объекта
|
|
|
lJSONInput = ProcessCommunicator.ProcessParentReadWaitObject()
|
|
|
lProcessResponse["ActivitySpecificationDict"]=lJSONInput
|
|
|
#Выполнить вызов функции
|
|
|
lProcessResponse["Result"]=JSONNormalize.JSONNormalizeDictListStrIntBool(locals()[lJSONInput['ActivityName']](*lJSONInput['ArgumentList'],**lJSONInput['ArgumentDict']))
|
|
|
except Exception as e:
|
|
|
#Установить флаг ошибки
|
|
|
lProcessResponse["ErrorFlag"]=True
|
|
|
#Зафиксировать traceback
|
|
|
lProcessResponse["ErrorTraceback"]=traceback.format_exc()
|
|
|
#Зафиксировать Error message
|
|
|
lProcessResponse["ErrorMessage"]=str(e)
|
|
|
#lProcessResponse["ErrorArgs"]=str(e.args)
|
|
|
#Отправить ответ в родительский процесс
|
|
|
ProcessCommunicator.ProcessParentWriteObject(lProcessResponse)
|
|
|
|
|
|
else:
|
|
|
print('Debug mode is turned on!')
|
|
|
|
|
|
#if __name__ == '__main__':
|
|
|
# if len(sys.argv) > 1:
|
|
|
# lFunctionArgs = sys.argv[2:]
|
|
|
# print(locals()[sys.argv[1]](*lFunctionArgs))
|
|
|
|
|
|
|