diff --git a/Robot/GUI.py b/Robot/GUI.py index 1f6e5b8f..08266e9c 100644 --- a/Robot/GUI.py +++ b/Robot/GUI.py @@ -20,6 +20,16 @@ from threading import Timer #################################### # GUI Module - interaction with Desktop application +#GUI Naming convention +#__ + +#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 #inActivitySpecificationDict: #{ @@ -43,185 +53,42 @@ from threading import Timer # Result: - required if ErrorFlag is false #} -mFlagIsDebug=False - -mPywinautoApplication=pywinauto.Application(backend="win32") -#mPywinautoActiveBackend="win32" -mPywinautoActiveBackend="uia" -#mPywinautoApplication=pywinauto.Application(backend="uia") - -##### -##Внимание! Нельзя делать print так как он уходит в родительский поток и ломает механизм взаимодействия -##Чтобы его использовать надо писать функцию обертку -##### - -########################### -####Модуль Automation -########################### - -#inElementSpecificationList = [ [{lvl_0},{lvl_1},{lvl_2}],... ] -#result = pywinauto element wrapper instance or None -def AutomationSearchMouseElement(inElementSpecification,inFlagIsSearchOnline=True): - lGUISearchElementSelected=None - #Настройка - частота обновления подсвечивания - lTimeSleepSeconds=0.4 - lElementFoundedList=[] - #Ветка поиска в режиме реального времени - if inFlagIsSearchOnline: - #Сбросить нажатие Ctrl, если оно было - bool(win32api.GetAsyncKeyState(17)) - lFlagLoop = True - while lFlagLoop: - #Проверить, нажата ли клавиша Ctrl (код 17) - lFlagKeyPressedCtrl=bool(win32api.GetAsyncKeyState(17)) - #Подсветить объект, если мышка наведена над тем объектом, который не подсвечивался в прошлый раз - if not lFlagKeyPressedCtrl: - #Получить координаты мыши - (lX,lY) = win32api.GetCursorPos() - lElementFounded={} - #Создать карту пикселей и элементов - #####Внимание! Функция GUISearchElementByRootXY не написана - lElementFoundedList=GUISearchElementByRootXY(PywinautoExtElementGet(inElementSpecification),lX,lY) - #print(lElementFoundedList) - lElementFounded=lElementFoundedList[-1]["element"] - #Подсветить объект, если он мышь раньше стояла на другом объекте - if lGUISearchElementSelected != lElementFounded: - lGUISearchElementSelected = lElementFounded - #Доработанная функция отрисовки - if lElementFounded is not None: - draw_outline_new(lElementFounded) - else: - #Была нажата клавиша Ctrl - выйти из цикла - lFlagLoop=False; - #Заснуть до следующего цикла - time.sleep(lTimeSleepSeconds) - #Ветка поиска по заранее созданной карте - else: - ################################### - #Внимание Старая ветка (неправильный результат) - ################################### - lBitmap={} - #Создать карту пикселей и элементов - lBitmap=GUISearchBitmapCreate(PywinautoExtElementGet(inElementSpecification),lBitmap) - #Выдать сообщение, что поиск готов к использованию - #print("GUISearch: Ready for search!") - ########### - #Версия с задержкой (без таймеров, событий в отдельных потоках) - ########### - #Сбросить нажатие Ctrl, если оно было - bool(win32api.GetAsyncKeyState(17)) - lFlagLoop = True - while lFlagLoop: - #Проверить, нажата ли клавиша Ctrl (код 17) - lFlagKeyPressedCtrl=bool(win32api.GetAsyncKeyState(17)) - #Подсветить объект, если мышка наведена над тем объектом, который не подсвечивался в прошлый раз - if not lFlagKeyPressedCtrl: - #Получить координаты мыши - (lX,lY) = win32api.GetCursorPos() - #Подсветить объект, если мышь над ним - if (lX,lY) in lBitmap: - if lGUISearchElementSelected != lBitmap[lX,lY]: - lGUISearchElementSelected = lBitmap[lX,lY] - #Классическая функция отрисовки (из pywinauto) - #lBitmap[lX,lY].draw_outline() - #Доработанная функция отрисовки - draw_outline_new(lBitmap[lX,lY]) - else: - lGUISearchElementSelected = None - else: - #Была нажата клавиша Ctrl - выйти из цикла - lFlagLoop=False; - #Заснуть до следующего цикла - time.sleep(lTimeSleepSeconds) - #Вернуть результат поиска - return lElementFoundedList - -def AutomationSearchMouseElementHierarchy(inElementSpecification,inFlagIsSearchOnline=True): - lItemInfo = [] - #Запустить функцию поиска элемента по мыши - lElementList = AutomationSearchMouseElement(inElementSpecification,inFlagIsSearchOnline) - lElement = lElementList[-1]['element'] - #Detect backend of the elements - lFlagIsBackendWin32 = True - #Если объект имеется (не None), то выполнить построение иерархии - #pdb.set_trace() - 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(ElementInfoExportObject(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 - #Вернуть результат - return lItemInfo - #return [1,2,3,4,5,3] +#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" -########################### -####Модуль PywinautoExt -########################### -def PywinautoExtElementCtrlIndexGet(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): - lResult = None - lFlagFind = False - else: - lResult = lResult + 1 - else: - lResult=-1 - lFlagFind=False - #Вернуть результат - return lResult +############################ +#Новая версия +############################ #Получить список элементов, который удовлетворяет условиям через расширенный движок поиска -#[ { -#"index":<Позиция элемента в родительском объекте>, -# "depth_start" - глубина, с которой начинается поиск (по умолчанию 1) -# "depth_end" - глубина, до которой ведется поиск (по умолчанию 1) -# "class_name" - наименование класса, который требуется искать -# "title" - наименование заголовка -# "rich_text" - наименование rich_text -#} ] -def PywinautoExtElementsGet (inSpecificationList,inElement=None): +#[ +# { +# "index":<Позиция элемента в родительском объекте>, +# "depth_start" - глубина, с которой начинается поиск (по умолчанию 1) +# "depth_end" - глубина, до которой ведется поиск (по умолчанию 1) +# "class_name" - наименование класса, который требуется искать +# "title" - наименование заголовка +# "rich_text" - наименование rich_text +# } +#] +################ +#return: List of UI Object +#old name - PywinautoExtElementsGet +def UIOSelector_Get_UIOList (inSpecificationList,inElement=None): lResultList=[] lChildrenList=[] #Получить родительский объект если на вход ничего не поступило @@ -307,95 +174,37 @@ def PywinautoExtElementsGet (inSpecificationList,inElement=None): else: lResultList.extend(lChildrenList) return lResultList -#Получить список информационных объектов, который удовлетворяет условиям -def PywinautoExtElementsGetInfo (inSpecificationList,inElement=None): - #Получить родительский объект если на вход ничего не поступило - lResultList=PywinautoExtElementsGet(inSpecificationList,inElement) - lIterator = 0 - for lItem in lResultList: - lResultList[lIterator]=ElementInfoExportObject(lResultList[lIterator].element_info) - lIterator = lIterator + 1 - return lResultList -#Получить элемент через расширенный движок поиска -#[ { -#"index":<Позиция элемента в родительском объекте>, -# -#} ] -def PywinautoExtElementGet (inSpecificationList,inElement=None): + +################################################################################################# +#Get first (in more than one) UIO (UI Object) +#inSpecificationList - UIOSelector +#old name - PywinautoExtElementGet +def UIOSelector_Get_UIO (inSpecificationList,inElement=None): lResult=None #Получить родительский объект если на вход ничего не поступило lResultList=PywinautoExtElementsGet(inSpecificationList,inElement) if len(lResultList)>0: lResult=lResultList[0] return lResult -#Проверить, существует ли объект -def PywinautoExtElementExist (inSpecificationList): - #pdb.set_trace() - return len(PywinautoExtElementsGet(inSpecificationList))>0 -#Ожидать появления элемента -def PywinautoExtElementWaitAppear(inSpecificationList,inTimeout=60): - lTimeoutSeconds = 0 - while (not PywinautoExtElementExist(inSpecificationList) and inTimeout>lTimeoutSeconds): - lTimeoutSeconds = lTimeoutSeconds + 0.5 - #Заснуть на полсекунды - time.sleep(0.5) - return PywinautoExtElementExist(inSpecificationList) -#Функция, которая попытается восстановить окно, если оно есть, но свернуто (особенность uia backend - он не может прицепиться к окну, если оно свернуто) -def PywinautoExtTryToRestore(inSpecificationList): - #pdb.set_trace() - try: - #Подготовка взодного массива - inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inSpecificationList) - #Выполнить подключение к объекту. Восстановление необходимо только в бэке win32, так как в uia свернутое окно не распознается - lRPAApplication = pywinauto.Application(backend="win32") - lRPAApplication.connect(**inSpecificationList[0]) - lRPAApplication.top_window().restore() - except Exception: - True==False - -################################ -#Функция повторяющегося таймера -############################# -class RepeatedTimer(object): - def __init__(self, interval, function, *args, **kwargs): - self._timer = None - self.interval = interval - self.function = function - self.args = args - self.kwargs = kwargs - self.is_running = False - self.start() - - def _run(self): - self.is_running = False - self.start() - self.function(*self.args, **self.kwargs) - - def start(self): - if not self.is_running: - self._timer = Timer(self.interval, self._run) - self._timer.start() - self.is_running = True - - def stop(self): - self._timer.cancel() - self.is_running = False -#mTimer.start() # after 30 seconds, "hello, world" will be printed -#Флаг отладки напрямую (не выполнять чтение буфера stdin) -################################## -###Методы взаимодействия с GUI интерфейсом -################################## -#pywinauto -def GetControl(inControlSpecificationArray): - #Подготовка взодного массива +################################################################################################## +#inControlSpecificationArray - List of dict, dict in pywinauto.find_windows notation +#Backend selection - attribute "backend" ("win32" || "uia") in 1-st list element +#return UIO object +#old name - GetControl +def PWASpecification_Get_UIO(inControlSpecificationArray): + #Определение backend + lBackend=mDefaultPywinautoBackend + if "backend" in inControlSpecificationArray[0]: + lBackend=inControlSpecificationArray[0]["backend"] + #Подготовка входного массива inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inControlSpecificationArray) #Выполнить идентификацию объектов, если передан массив lResultList=[]; lTempObject=None if len(inControlSpecificationArray) > 0: #Выполнить подключение к объекту - lRPAApplication = pywinauto.Application(backend=mPywinautoActiveBackend) + lRPAApplication = pywinauto.Application(backend=lBackend) #Проверка разрядности try: lRPAApplication.connect(**inControlSpecificationArray[0]) @@ -416,8 +225,179 @@ def GetControl(inControlSpecificationArray): lTempObject=lTempObject.window(**lWindowSpecification) return lTempObject -#Получить массив свойств и методов у элемента -def ElementActionGetList (inControlSpecificationArray): +########################################################################################################### +#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)) + lFlagLoop = True + while lFlagLoop: + #Проверить, нажата ли клавиша Ctrl (код 17) + lFlagKeyPressedCtrl=bool(win32api.GetAsyncKeyState(17)) + #Подсветить объект, если мышка наведена над тем объектом, который не подсвечивался в прошлый раз + if not lFlagKeyPressedCtrl: + #Получить координаты мыши + (lX,lY) = win32api.GetCursorPos() + lElementFounded={} + #Создать карту пикселей и элементов + #####Внимание! Функция GUISearchElementByRootXY не написана + lElementFoundedList=GUISearchElementByRootXY(PywinautoExtElementGet(inElementSpecification),lX,lY) + #print(lElementFoundedList) + lElementFounded=lElementFoundedList[-1]["element"] + #Подсветить объект, если он мышь раньше стояла на другом объекте + if lGUISearchElementSelected != lElementFounded: + lGUISearchElementSelected = lElementFounded + #Доработанная функция отрисовки + if lElementFounded is not None: + draw_outline_new(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,inFlagIsSearchOnline) + lElement = lElementList[-1]['element'] + #Detect backend of the elements + lFlagIsBackendWin32 = True + #Если объект имеется (не None), то выполнить построение иерархии + #pdb.set_trace() + 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(ElementInfoExportObject(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 + #Вернуть результат + 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): + 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=PywinautoExtElementsGet(inSpecificationList,inElement) + lIterator = 0 + for lItem in lResultList: + lResultList[lIterator]=ElementInfoExportObject(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(PywinautoExtElementsGet(inSpecificationList))>0 + +#################################################################################################### +#Wait for the UIO by the UIOSelector appear +#inSpecificationList - UIOSelector +#result - { } +#old name - PywinautoExtElementWaitAppear +def UIOSelector_WaitAppear_Dict(inSpecificationList,inTimeout=60): + lTimeoutSeconds = 0 + while (not PywinautoExtElementExist(inSpecificationList) and inTimeout>lTimeoutSeconds): + lTimeoutSeconds = lTimeoutSeconds + 0.5 + #Заснуть на полсекунды + time.sleep(0.5) + return PywinautoExtElementExist(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=ElementSpecificationArraySearchPrepare(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=PywinautoExtElementGet(inControlSpecificationArray) lActionList=dir(lObject) @@ -431,8 +411,14 @@ def ElementActionGetList (inControlSpecificationArray): if lActionItem[0].isupper(): lResult.remove(lActionItem) return lResult -#Выполнить действие над элементом -def ElementRunAction(inControlSpecificationArray,inActionName,inArgumentList=[],inkwArgumentObject={}): + +#################################################################################################### +#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=PywinautoExtElementGet(inControlSpecificationArray) #Получить метод для вызова @@ -455,8 +441,12 @@ def ElementRunAction(inControlSpecificationArray,inActionName,inArgumentList=[], return lResult else: raise e + return lResult -def ElementGetInfo(inControlSpecificationArray): +#################################################################################################### +#Get the UIO dict of the attributes +#old name - ElementGetInfo +def UIOSelector_Get_UIOInfo(inControlSpecificationArray): #Подготовка входного массива inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inControlSpecificationArray) #Выполнить идентификацию объектов, если передан массив @@ -469,6 +459,16 @@ def ElementGetInfo(inControlSpecificationArray): #Добавить информацию об обнаруженом объекте lResultList.append(ElementInfoExportObject(lTempObjectInfo)); return lResultList + +############################ +#Старая версия +############################ +mFlagIsDebug=False + + + +################################## + ############################################ ###Модуль поиска объекта на экране ############################################