|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
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 threading import Timer
|
|
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
###########################
|
|
|
####Модуль 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):
|
|
|
#Прекратить поиск, если элемент был обнаружен
|
|
|
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
|
|
|
#Получить список элементов, который удовлетворяет условиям через расширенный движок поиска
|
|
|
#[ {
|
|
|
#"index":<Позиция элемента в родительском объекте>,
|
|
|
# "depth_start" - глубина, с которой начинается поиск (по умолчанию 1)
|
|
|
# "depth_end" - глубина, до которой ведется поиск (по умолчанию 1)
|
|
|
# "class_name" - наименование класса, который требуется искать
|
|
|
# "title" - наименование заголовка
|
|
|
# "rich_text" - наименование rich_text
|
|
|
#} ]
|
|
|
def PywinautoExtElementsGet (inSpecificationList,inElement=None):
|
|
|
lResultList=[]
|
|
|
lChildrenList=[]
|
|
|
#Получить родительский объект если на вход ничего не поступило
|
|
|
if inElement is None:
|
|
|
#сформировать спецификацию на получение элемента
|
|
|
lRootElementSpecification=[inSpecificationList[0]]
|
|
|
lRootElement=GetControl(lRootElementSpecification)
|
|
|
if lRootElement is not None:
|
|
|
lChildrenList.append(lRootElement.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:
|
|
|
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:
|
|
|
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
|
|
|
#pdb.set_trace()
|
|
|
#Циклический обход по детям, на предмет соответствия всем условиям
|
|
|
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
|
|
|
#pdb.set_trace()
|
|
|
#Циклический вызов для всех детей со скорректированной спецификацией
|
|
|
lResultList.extend(PywinautoExtElementsGet(lChildrenItemNewSpecificationList,lChildrenItem))
|
|
|
#Фильтрация
|
|
|
if lFlagGoCheck:
|
|
|
lFlagAddChild=True
|
|
|
#Фильтрация по title
|
|
|
if 'title' in inSpecificationList[0]:
|
|
|
if lChildrenItem.element_info.name != inSpecificationList[0]["title"]:
|
|
|
lFlagAddChild=False
|
|
|
#Фильтрация по rich_text
|
|
|
if 'rich_text' in inSpecificationList[0]:
|
|
|
if lChildrenItem.element_info.rich_text != inSpecificationList[0]["rich_text"]:
|
|
|
lFlagAddChild=False
|
|
|
#Фильтрация по class_name
|
|
|
if 'class_name' in inSpecificationList[0]:
|
|
|
if lChildrenItem.element_info.class_name != inSpecificationList[0]["class_name"]:
|
|
|
lFlagAddChild=False
|
|
|
#Фильтрация по friendly_class_name
|
|
|
if 'friendly_class_name' in inSpecificationList[0]:
|
|
|
if lChildrenItem.friendly_class_name() != inSpecificationList[0]["friendly_class_name"]:
|
|
|
lFlagAddChild=False
|
|
|
#Фильтрация по control_type
|
|
|
if 'control_type' in inSpecificationList[0]:
|
|
|
if lChildrenItem.element_info.control_type != inSpecificationList[0]["control_type"]:
|
|
|
lFlagAddChild=False
|
|
|
#####
|
|
|
#Все проверки пройдены - флаг добавления
|
|
|
if lFlagAddChild:
|
|
|
lChildrenList.append(lChildrenItem)
|
|
|
#Выполнить рекурсивный вызов (уменьшение количества спецификаций), если спецификация больше одного элемента
|
|
|
if len(inSpecificationList)>1 and len(lChildrenList)>0 is not None:
|
|
|
#Вызвать рекурсивно функцию получения следующего объекта, если в спецификации есть следующий объект
|
|
|
for lChildElement in lChildrenList:
|
|
|
lResultList.extend(PywinautoExtElementsGet(inSpecificationList[1:],lChildElement))
|
|
|
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):
|
|
|
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)
|
|
|
|
|
|
############################################
|
|
|
####Межпроцессное взаимодействие
|
|
|
############################################
|
|
|
#ProcessParentReadWaitString
|
|
|
def ProcessParentReadWaitString():
|
|
|
#Выполнить чтение строки
|
|
|
#ctypes.windll.user32.MessageBoxW(0, "Hello", "Your title", 1)
|
|
|
lResult = sys.stdin.buffer.readline()
|
|
|
#Вернуть потенциальные \n
|
|
|
lResult = lResult.replace(b'{{n}}',b'\n')
|
|
|
lResult = zlib.decompress(lResult[0:-1])
|
|
|
lResult = lResult.decode("utf-8")
|
|
|
#Вернуть результат
|
|
|
return lResult
|
|
|
|
|
|
#ParentProcessWriteString
|
|
|
def ProcessParentWriteString(lString):
|
|
|
lByteString = zlib.compress(lString.encode("utf-8"))
|
|
|
#Выполнить отправку строки в родительский процесс
|
|
|
#Вернуть потенциальные \n
|
|
|
lByteString = lByteString.replace(b'\n',b'{{{n}}}')
|
|
|
#Вернуть \r
|
|
|
lByteString = lByteString.replace(b'\r',b'{{{r}}}')
|
|
|
#Вернуть \0
|
|
|
lByteString = lByteString.replace(b'\0',b'{{{0}}}')
|
|
|
#Вернуть \a
|
|
|
lByteString = lByteString.replace(b'\a',b'{{{a}}}')
|
|
|
#Вернуть \b
|
|
|
lByteString = lByteString.replace(b'\b',b'{{{b}}}')
|
|
|
#Вернуть \t
|
|
|
lByteString = lByteString.replace(b'\t',b'{{{t}}}')
|
|
|
#Вернуть \v
|
|
|
lByteString = lByteString.replace(b'\v',b'{{{v}}}')
|
|
|
#Вернуть \f
|
|
|
lByteString = lByteString.replace(b'\f',b'{{{f}}}')
|
|
|
############################
|
|
|
#print(b"Result: "+lByteString)
|
|
|
#lByteString= b'x\x9c\xdd\x95]O\xc20\x14\x86\xffJ\xb3[5\xa1Cqz\x07\xc4\xe8\x8d\x1fQ\x13.\x0cYJw\xb6U\xbav\xe9\xce"\x84\xf0\xdfm\'"\xb8\xa0L%Q\xb3\x9b\xf6=\xdfO\x9a\xb3\x99\x17\x97\x8a\xa3\xd0\xea\x8ae\xe0\x9d\x12\xaf[\xa2\xce\x98S\xee\x80\x19\x9e^\xea\xb2\x803\t\x19(\xbc\x10`\x9c6\xf5\xf6\x89\xc7LRt\x8daS\x1b\xf5\xf00\xf3\xd4"\xc1u\x0e\xea\xf6\xa6K\x0e\xc8\xb9\xd6\x89\x04\xd2O\x8d\xb6&\x1bb\x04OC\x84\t~\xe2\x97\x1b\xcd\xa1(B\x11YG\xdaj\xfb\xc1\x9b\xb8\xa2\xa4LE\xd2\xd5\xa4\xf6\xdenY\x85Kf\xc3^;yI\x18\x0eD\x94\x00\x0e\x84{{n}}\xa9K\xce\xb5B\xa3e\x88\xd3\xbc\xf2Z\xd5\xaa\x82\xaa\x94\xd25\x0b\x1c\x99J\xaa\x023OB\xec\xbavEP\xe7\x8b\x93\x11I\xeaTz\xe2\xbb\xebH\xa3eW5\xe8\xb7\xe6\xce^*\x14\xb6\x83e\xda\xf9phe]b^\xe2\xf5\xe8\xd1Vp\xf0\xfe.\xbb\x1b\xa6`\x87\xfc8\x1a\x9bSE0q\xa2\x15\xeer\xe0"\x16\xbcz\x9f\xfdT\xc8h\x9d\xdf\xc7\xd4\xbe\xcdj1\xd9:\xa9\x1f\xe1B7\x81\xa1\xef\xc0\xd0:\x98\xc3-\xc0\xd4X\xfc\xda\xf1i\xbb\xe9\xfc\xdb<\x8c\xff2\x7f\'\xa8\x8d\xdf\xdab\xfc\x9e\xd6\xe3\x8c\x99qQ\xe3\xb0f\xd9\x19\x90{\xade\x8f\x99/3\xa1AC(\xfe\x16P\x06F \x90\xb3\t\x07Iba\x17\x83P\xa4\xbf\xb7G\x9e\x04\xa6vE\x13\xb6\xfc\x13\xd6\xa85\x0b\xdd\x19\xd6^i\x11\xa8FT;G\xfe\x06\xac\xc1q\xb0N\x956\xd84\xae\xe4p\xbe\xfa=\x03\x01\xce\x95\x9a'
|
|
|
#lByteString = b"x\x9c\xb5\x91\xcfO\xc3 \x14\xc7\xff\x95\xa6\xd7uI\xf9Q\x8a\xde\xd4\x93\x07\xbdx\xf00\x97\x05)[I(\x90\x8ef3\xcb\xfew\x81M\xbb\xd9M]\x8c!y\xd0\xf7}\xbc\xef\xe3\xd3\xc9&\xd5\xac\x11\xe9u\x92j\xb1J@2N\x1e\x8d\x13\x96U\xa3Q\x9a%i+y=sb\xed\xceV\xd8\xd6p\xb1\\\xced\xe5K{{n}}\x80`\x9f\xeb\x135\xd3\x95{{n}}.\x08RR\xe4>\xc3\x15\xf3\x97>\xbc\x8f:r\xa3]k\xd4\xcc\xbd\xd9(>K]\x99\xd5\xa1\x12\xbd\x00\xc6\xb0\xcc\xcb0\xa4\xe0\x8e\xe9E4\xd8\xa4J\xcc\xc3\xb44\x07^r\xc6\xfa3\x04(\xbeeQ\x07\x05P\x1a\xa4W\xe3\x9ci\xfc\xf7\x15(\xb6A\xee\xb4\x93\x8d\xd85\x9f`?\xf6n\xd8i0v\xadw\xd5\x95X\x87n>\xf1d\x05\x97s\xc9\x99\x93F\xdf\xd5R\xc5K=\xcc\x1bk\xd5^\x1d`\xfc\xa2]\x06PwJ\r\xf0\x9d\xa2\xf6 tw\xcb\xda\x01\xb6}\x83\xd3\xcc\x00\xec\x99\x15\xf4\x88Y\x99\x1f2\x83\xb4\xfc\x8e\x99\xdf\xb3d\x0c\x01.1E\x04\x93l\xff\x8e\xcf\x7f6\xa4Z\xfc\x82\xeaK\x97c BD\xf3\x101\x89g\xba\x8b\x03\xd0?\x97\xff#\xfb{'\x9a\x8b\xe0\x03H\xc89\xfa\x08\x15\x7f\xa2\x0f >\x80_\x0e\xe0\x93\xb3\xf0\xc3\xc4\xd3m\\\xef\xf8\x958\xa0"
|
|
|
#lt=open("logSendByteStringWithoutN.log","wb")
|
|
|
#lt.write(lByteString)
|
|
|
#lt.close()
|
|
|
############################
|
|
|
sys.stdout.buffer.write(lByteString+bytes("\n","utf-8"))
|
|
|
sys.stdout.flush();
|
|
|
return
|
|
|
#ProcessParentWriteObject
|
|
|
def ProcessParentWriteObject(inObject):
|
|
|
#Выполнить отправку сконвертированного объекта в JSON
|
|
|
ProcessParentWriteString(json.dumps(inObject))
|
|
|
return
|
|
|
#ProcessParentReadWaitObject
|
|
|
def ProcessParentReadWaitObject():
|
|
|
#Выполнить получение и разбор объекта
|
|
|
lResult=json.loads(ProcessParentReadWaitString());
|
|
|
return lResult;
|
|
|
|
|
|
##################################
|
|
|
###Методы взаимодействия с GUI интерфейсом
|
|
|
##################################
|
|
|
#pywinauto
|
|
|
def GetControl(inControlSpecificationArray):
|
|
|
#Подготовка взодного массива
|
|
|
inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inControlSpecificationArray)
|
|
|
#Выполнить идентификацию объектов, если передан массив
|
|
|
lResultList=[];
|
|
|
lTempObject=None
|
|
|
if len(inControlSpecificationArray) > 0:
|
|
|
#Выполнить подключение к объекту
|
|
|
lRPAApplication = pywinauto.Application(backend=mPywinautoActiveBackend)
|
|
|
#Проверка разрядности
|
|
|
try:
|
|
|
lRPAApplication.connect(**inControlSpecificationArray[0])
|
|
|
except Exception as e:
|
|
|
PywinautoExtTryToRestore(inControlSpecificationArray)
|
|
|
try:
|
|
|
lRPAApplication.connect(**inControlSpecificationArray[0])
|
|
|
except Exception as e:
|
|
|
lRPAApplication = None
|
|
|
if lRPAApplication is not None:
|
|
|
#lTempObject=lRPAApplication.window(**inControlSpecificationArray[0])
|
|
|
#Скорректировано из-за недопонимания структуры
|
|
|
lTempObject=lRPAApplication
|
|
|
#Нормализация массива для целей выборки объекта (удаление конфликтующих ключей)
|
|
|
inControlSpecificationArray=ElementSpecificationListNormalize(inControlSpecificationArray)
|
|
|
#Циклическое прохождение к недрам объекта
|
|
|
for lWindowSpecification in inControlSpecificationArray[0:]:
|
|
|
lTempObject=lTempObject.window(**lWindowSpecification)
|
|
|
return lTempObject
|
|
|
|
|
|
#Получить массив свойств и методов у элемента
|
|
|
def ElementActionGetList (inControlSpecificationArray):
|
|
|
#Получить объект
|
|
|
lObject=PywinautoExtElementGet(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
|
|
|
#Выполнить действие над элементом
|
|
|
def ElementRunAction(inControlSpecificationArray,inActionName,inArgumentList=[],inkwArgumentObject={}):
|
|
|
#Определить объект
|
|
|
lObject=PywinautoExtElementGet(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
|
|
|
|
|
|
def ElementGetInfo(inControlSpecificationArray):
|
|
|
#Подготовка входного массива
|
|
|
inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inControlSpecificationArray)
|
|
|
#Выполнить идентификацию объектов, если передан массив
|
|
|
lResultList=[];
|
|
|
if len(inControlSpecificationArray) > 0:
|
|
|
#Получить объект
|
|
|
lTempObject=PywinautoExtElementGet(inControlSpecificationArray)
|
|
|
#Получить инфо объект
|
|
|
lTempObjectInfo = lTempObject.element_info
|
|
|
#Добавить информацию об обнаруженом объекте
|
|
|
lResultList.append(ElementInfoExportObject(lTempObjectInfo));
|
|
|
return lResultList
|
|
|
############################################
|
|
|
###Модуль поиска объекта на экране
|
|
|
############################################
|
|
|
#Инициализация глобальных переменных
|
|
|
mBitmap={}
|
|
|
mGUISearchElementSelected={}
|
|
|
def GUISearchEvent_on_move(lX, lY):
|
|
|
global mBitmap
|
|
|
global mGUISearchElementSelected
|
|
|
if (lX,lY) in mBitmap:
|
|
|
if mGUISearchElementSelected != mBitmap[lX,lY]:
|
|
|
mGUISearchElementSelected = mBitmap[lX,lY]
|
|
|
mBitmap[lX,lY].draw_outline()
|
|
|
def GUISearchEvent_on_click(x, y, button, pressed):
|
|
|
global mBitmap
|
|
|
global mGUISearchElementSelected
|
|
|
print('{0} at {1}'.format(
|
|
|
'Pressed' if pressed else 'Released',
|
|
|
(x, y)))
|
|
|
print(str(len(mBitmap)))
|
|
|
if not pressed:
|
|
|
# Stop listener
|
|
|
del mBitmap
|
|
|
mBitmap={}
|
|
|
del mGUISearchElementSelected
|
|
|
mGUISearchElementSelected={}
|
|
|
return False
|
|
|
def GUISearchEvent_on_scroll(x, y, dx, dy):
|
|
|
print('Scrolled {0} at {1}'.format(
|
|
|
'down' if dy < 0 else 'up',
|
|
|
(x, y)))
|
|
|
|
|
|
#Создать bitmap карту объектов
|
|
|
#GUISearchBitmapCreate
|
|
|
#inElement - wrapper_object()
|
|
|
# dict: x_y_ : elementObject
|
|
|
def GUISearchRun(inSpecificationArray):
|
|
|
lBitmap={}
|
|
|
lGUISearchElementSelected=None
|
|
|
#Создать карту пикселей и элементов
|
|
|
lBitmap=GUISearchBitmapCreate(GetControl(inSpecificationArray),lBitmap)
|
|
|
#Выдать сообщение, что поиск готов к использованию
|
|
|
#print("GUISearch: Ready for search!")
|
|
|
#mBitmap=lBitmap
|
|
|
# Collect events until released
|
|
|
#Версия с Events (занимает поток, чтобы использовать общие переменные)
|
|
|
#with mouse.Listener(
|
|
|
# on_move=GUISearchEvent_on_move,
|
|
|
# on_click=GUISearchEvent_on_click,
|
|
|
# on_scroll=GUISearchEvent_on_scroll) as listener:
|
|
|
#listener.join()
|
|
|
# True == False
|
|
|
#####################
|
|
|
###Версия с таймером
|
|
|
#####################
|
|
|
#mTimer = RepeatedTimer(0.5, GUISearchDraw, lBitmap) # it auto-starts, no need of rt.start()
|
|
|
#mTimer.start()
|
|
|
###########
|
|
|
#Версия с задержкой
|
|
|
###########
|
|
|
#Сбросить нажатие Ctrl, если оно было
|
|
|
bool(win32api.GetAsyncKeyState(17))
|
|
|
lTimeSleepSeconds=0.7
|
|
|
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]
|
|
|
lBitmap[lX,lY].draw_outline()
|
|
|
else:
|
|
|
lGUISearchElementSelected = None
|
|
|
else:
|
|
|
#Была нажата клавиша Ctrl - выйти из цикла
|
|
|
lFlagLoop=False;
|
|
|
#Заснуть до следующего цикла
|
|
|
time.sleep(lTimeSleepSeconds)
|
|
|
#Вернуть результат поиска
|
|
|
return lGUISearchElementSelected
|
|
|
|
|
|
#############################################################
|
|
|
#Поиск элементов
|
|
|
#############################################################
|
|
|
#inHierarchyList: [{"index":<>,"element":<>}]
|
|
|
#
|
|
|
#Вернуть словарь [{"index":<>,"element":<>}]
|
|
|
#Последний элемент - искомый
|
|
|
def GUISearchElementByRootXY(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 = GUISearchElementByRootXY(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
|
|
|
#Техническая функция
|
|
|
|
|
|
def GUISearchBitmapCreate (inElement,inBitmapDict={}):
|
|
|
#Добавить в карту информацию о текущем объекте
|
|
|
inBitmapDict=GUISearchBitmapElementFill(inElement,inBitmapDict)
|
|
|
#Получить список детей и добавить в карту
|
|
|
for lItem in inElement.children():
|
|
|
inBitmapDict=GUISearchBitmapCreate(lItem,inBitmapDict)
|
|
|
return inBitmapDict
|
|
|
|
|
|
#GUISearchBitmapElementFill
|
|
|
def GUISearchBitmapElementFill (inElement,inBitmapDict):
|
|
|
lElementInfo=inElement.element_info
|
|
|
#pdb.set_trace()
|
|
|
#Получить параметры прямоугольника
|
|
|
lElementRectX1 = 0
|
|
|
lElementRectX2 = 0
|
|
|
lElementRectY1 = 0
|
|
|
lElementRectY2 = 0
|
|
|
lFlagHasRect = True
|
|
|
try:
|
|
|
lElementRectX1=lElementInfo.rectangle.left
|
|
|
lElementRectX2=lElementInfo.rectangle.right
|
|
|
lElementRectY1=lElementInfo.rectangle.top
|
|
|
lElementRectY2=lElementInfo.rectangle.bottom
|
|
|
#Выполнить установку элемента, если прямоугольник получить удалось
|
|
|
if lFlagHasRect:
|
|
|
lX=lElementRectX1
|
|
|
#Циклический обход по оси X
|
|
|
while lX<=lElementRectX2:
|
|
|
lY=lElementRectY1
|
|
|
#Циклический обход по Оси Y
|
|
|
while lY<=lElementRectY2:
|
|
|
######################
|
|
|
#Если объект в карте есть
|
|
|
if (lX,lY) in inBitmapDict:
|
|
|
if inBitmapDict[lX,lY] 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
|
|
|
#Получить координаты ИО
|
|
|
lElementOldInfo = inBitmapDict[lX,lY].element_info
|
|
|
#lElementNew = inElement
|
|
|
lElementOldX1 = lElementOldInfo.rectangle.left
|
|
|
lElementOldX2 = lElementOldInfo.rectangle.right
|
|
|
lElementOldY1 = lElementOldInfo.rectangle.top
|
|
|
lElementOldY2 = lElementOldInfo.rectangle.bottom
|
|
|
#Проверка вхождения по типу 1
|
|
|
if (lElementOldX1>=lElementRectX1) and (lElementOldY1>=lElementRectY1) and (lElementOldX2<=lElementRectX2) and (lElementOldY2<=lElementRectY2):
|
|
|
#Выполнить корректировку каретки по lY, чтобы не проходить по всем пикселям в рамках этой линии
|
|
|
lY = lElementOldY2 + 1
|
|
|
#Проверка вхождения по типу 3
|
|
|
elif (lElementOldX1<lElementRectX1) and (lElementOldY1<lElementRectY1) and (lElementOldX2>lElementRectX2) and (lElementOldY2>lElementRectY2):
|
|
|
#Установка элемента по адресу [<x>,<y>]
|
|
|
inBitmapDict[lX,lY]=inElement
|
|
|
#Инкремент y
|
|
|
lY=lY+1
|
|
|
#Проверка вхождения по типу 2
|
|
|
else:
|
|
|
#Установка элемента по адресу [<x>,<y>]
|
|
|
inBitmapDict[lX,lY]=inElement
|
|
|
#Инкремент y
|
|
|
lY=lY+1
|
|
|
#Если объекта в карте нет
|
|
|
else:
|
|
|
#Установка элемента по адресу [<x>,<y>]
|
|
|
inBitmapDict[lX,lY]=inElement
|
|
|
#Инкремент y
|
|
|
lY=lY+1
|
|
|
else:
|
|
|
#Установка элемента по адресу [<x>,<y>]
|
|
|
inBitmapDict[lX,lY]=inElement
|
|
|
#Инкремент y
|
|
|
lY=lY+1
|
|
|
######################
|
|
|
#Инкремент X
|
|
|
lX=lX+1
|
|
|
except Exception as e:
|
|
|
lFlagHasRect = False
|
|
|
return inBitmapDict
|
|
|
#debug
|
|
|
def ElementGetChildElementList(inControlSpecificationArray=[]):
|
|
|
#Подготовка входного массива
|
|
|
inControlSpecificationArray=ElementSpecificationArraySearchPrepare(inControlSpecificationArray)
|
|
|
#Выполнить идентификацию объектов, если передан массив
|
|
|
lResultList=[];
|
|
|
#ctypes.windll.user32.MessageBoxW(0, str(inControlSpecificationArray), "Your title", 1)
|
|
|
if len(inControlSpecificationArray) > 0:
|
|
|
#Получить объект
|
|
|
lTempObject = PywinautoExtElementGet(inControlSpecificationArray)
|
|
|
#Получить список дочерних объектов
|
|
|
lTempChildList = lTempObject.children()
|
|
|
lIterator=0
|
|
|
#Подготовить результирующий объект
|
|
|
for lChild in lTempChildList:
|
|
|
lTempObjectInfo=lChild.element_info
|
|
|
#Добавить информацию об обнаруженом объекте
|
|
|
lObjectInfoItem=ElementInfoExportObject(lTempObjectInfo)
|
|
|
#Итератор внутри объекта (для точной идентификации)
|
|
|
lObjectInfoItem['ctrl_index']=lIterator;
|
|
|
lResultList.append(lObjectInfoItem);
|
|
|
#Инкремент счетчика
|
|
|
lIterator=lIterator+1
|
|
|
else:
|
|
|
lResultList=GetRootElementList()
|
|
|
return lResultList
|
|
|
#Подготовить спецификацию для поиска элемента
|
|
|
def ElementSpecificationListNormalize(inElementSpecification):
|
|
|
lResult=[]
|
|
|
#Циклический обход
|
|
|
for lSpecificationItem in inElementSpecification:
|
|
|
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 == "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")
|
|
|
#Добавить строку в результирующий массив
|
|
|
lResult.append(lSpecificationItemNew)
|
|
|
#Вернуть результат
|
|
|
return lResult
|
|
|
#Подготовить массив для обращшения к поиску элемементов
|
|
|
def ElementSpecificationArraySearchPrepare(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")
|
|
|
#Добавить строку в результирующий массив
|
|
|
lResult.append(lSpecificationItemNew)
|
|
|
#Вернуть результат
|
|
|
return lResult
|
|
|
###############################
|
|
|
####Нормализация под JSON (в JSON нельзя передавать классы - только null, числа, строки, словари и массивы)
|
|
|
###############################
|
|
|
#Нормализация словаря под JSON
|
|
|
def JSONNormalizeDictionary(inDictionary):
|
|
|
#Сделать копию объекта
|
|
|
lResult=inDictionary.copy()
|
|
|
#Перебор всех элементов
|
|
|
for lItemKey,lItemValue in inDictionary.items():
|
|
|
#Флаг удаления атрибута
|
|
|
lFlagRemoveAttribute=False
|
|
|
#Если строка или число или массив или объект или None - оставить
|
|
|
if (
|
|
|
type(lItemValue) is dict or
|
|
|
type(lItemValue) is int or
|
|
|
type(lItemValue) is str or
|
|
|
type(lItemValue) is list or
|
|
|
lItemValue is None):
|
|
|
True==True
|
|
|
else:
|
|
|
lFlagRemoveAttribute=True
|
|
|
#Рекурсивный вызов, если объект является словарем
|
|
|
if type(lItemValue) is dict:
|
|
|
lResult[lItemKey]=JSONNormalizeDictionary(lItemValue)
|
|
|
#Рекурсивный вызов, если объект является списком
|
|
|
if type(lItemValue) is list:
|
|
|
lResult[lItemKey]=JSONNormalizeList(lItemValue)
|
|
|
#############################
|
|
|
#Конструкция по удалению ключа из словаря
|
|
|
if lFlagRemoveAttribute:
|
|
|
lResult.pop(lItemKey)
|
|
|
#Вернуть результат
|
|
|
return lResult
|
|
|
#Нормализация массива под JSON
|
|
|
def JSONNormalizeList(inList):
|
|
|
lResult=[]
|
|
|
#Циклический обход
|
|
|
for lItemValue in inList:
|
|
|
#Если строка или число или массив или объект или None - оставить
|
|
|
if (
|
|
|
type(lItemValue) is int or
|
|
|
type(lItemValue) is str or
|
|
|
lItemValue is None):
|
|
|
lResult.append(lItemValue)
|
|
|
#Если является словарем - вызвать функцию нормализации словаря
|
|
|
if type(lItemValue) is dict:
|
|
|
lResult.append(JSONNormalizeDictionary(lItemValue))
|
|
|
#Если является массиваом - вызвать функцию нормализации массива
|
|
|
if type(lItemValue) is list:
|
|
|
lResult.append(JSONNormalizeList(lItemValue))
|
|
|
#Вернуть результат
|
|
|
return lResult
|
|
|
#Определить объект - dict or list - и нормализовать его для JSON
|
|
|
def JSONNormalizeDictList(inDictList):
|
|
|
lResult={}
|
|
|
if type(inDictList) is dict:
|
|
|
lResult=JSONNormalizeDictionary(inDictList)
|
|
|
if type(inDictList) is list:
|
|
|
lResult=JSONNormalizeList(inDictList)
|
|
|
return lResult;
|
|
|
#Получить объект из атрибутов, которые удалось прочитать
|
|
|
def ElementInfoExportObject(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
|
|
|
|
|
|
def GetRootElementList():
|
|
|
#Получить список объектов
|
|
|
lResultList=pywinauto.findwindows.find_elements(top_level_only=True,backend=mPywinautoActiveBackend)
|
|
|
lResultList2=[]
|
|
|
for lI in lResultList:
|
|
|
lTempObjectInfo=lI
|
|
|
lResultList2.append(ElementInfoExportObject(lI));
|
|
|
return lResultList2
|
|
|
def ElementDrawOutlineNew(inSpecificationArray):
|
|
|
draw_outline_new(PywinautoExtElementGet(inSpecificationArray))
|
|
|
return
|
|
|
def ElementDrawOutlineNewFocus(inSpecificationArray):
|
|
|
draw_outline_new_focus(PywinautoExtElementGet(inSpecificationArray))
|
|
|
return
|
|
|
def draw_outline_new(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)
|
|
|
#pdb.set_trace()
|
|
|
# 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)
|
|
|
#Аналог подсвечивания + установка фокуса
|
|
|
def draw_outline_new_focus(lWrapperObject,colour='green',thickness=2,fill=win32defines.BS_NULL,rect=None):
|
|
|
draw_outline_new(lWrapperObject,'green',2,win32defines.BS_NULL,None,True)
|
|
|
|
|
|
|
|
|
################
|
|
|
###GeneralClipboardGet
|
|
|
################
|
|
|
def GeneralClipboardGet():
|
|
|
win32clipboard.OpenClipboard()
|
|
|
lResult = win32clipboard.GetClipboardData()
|
|
|
win32clipboard.CloseClipboard()
|
|
|
return lResult
|
|
|
################
|
|
|
###GeneralClipboardSet
|
|
|
################
|
|
|
def GeneralClipboardSet(inText):
|
|
|
win32clipboard.OpenClipboard()
|
|
|
win32clipboard.EmptyClipboard()
|
|
|
win32clipboard.SetClipboardText(inText)
|
|
|
win32clipboard.CloseClipboard()
|
|
|
#Уснуть на 2 секунды
|
|
|
def GeneralSleep2s():
|
|
|
time.sleep(2)
|
|
|
|
|
|
#run()
|
|
|
lText = "Bitness:" + str(struct.calcsize("P") * 8)
|
|
|
#for line in sys.stdin:
|
|
|
# lText=lText+line;
|
|
|
#ctypes.windll.user32.MessageBoxW(0, lText, "Your title", 1)
|
|
|
|
|
|
buffer = ""
|
|
|
lJSONInputString=""
|
|
|
|
|
|
#Алгоритм включения debug режима (если передано ключевое слово debug как параметр)
|
|
|
#Если есть хотя бы один параметр (sys.argv[1+])
|
|
|
if len(sys.argv) > 1:
|
|
|
if sys.argv[1] == "debug":
|
|
|
mFlagIsDebug=True
|
|
|
#Выполнить чтение буфера, если не отладка библиотеки
|
|
|
if not mFlagIsDebug:
|
|
|
#{'functionName':'', 'argsArray':[]}
|
|
|
while True:
|
|
|
try:
|
|
|
lJSONInput = ProcessParentReadWaitObject()
|
|
|
lJSONInputString=str(lJSONInput)
|
|
|
#{'outputObject':''}
|
|
|
#Выполнить вызов функции
|
|
|
|
|
|
lResult=locals()[lJSONInput['functionName']](*lJSONInput['argsArray'])
|
|
|
lJSONInput['outputObject']=JSONNormalizeDictList(lResult)
|
|
|
ProcessParentWriteObject(lJSONInput)
|
|
|
except Exception as e:
|
|
|
#Вывод ошибки в родительский поток
|
|
|
ProcessParentWriteObject({'Error':str(e) + traceback.format_exc(), 'ArgObject':str(lJSONInputString)})
|
|
|
#ctypes.windll.user32.MessageBoxW(0, str(e), "Your title", 1)
|
|
|
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))
|
|
|
|
|
|
|