From c02f454592654f4f5ee7f467b38850d5b0be0557 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Fri, 8 Nov 2019 19:56:32 +0300 Subject: [PATCH] v1.0.15 #Pip work well Use cmd!!! --- .../INSTALLER | 0 .../pyOpenRPA-1.0.15.dist-info/METADATA | 80 ++ .../pyOpenRPA-1.0.15.dist-info/RECORD | 23 + .../WHEEL | 0 .../pyOpenRPA-1.0.15.dist-info/top_level.txt | 1 + .../pyOpenRPA-1.0.7.dist-info/METADATA | 23 - .../pyOpenRPA-1.0.7.dist-info/RECORD | 5 - .../Lib/site-packages/pyOpenRPA}/Clipboard.py | 0 .../Lib/site-packages/pyOpenRPA}/GUI.py | 4 +- .../pyOpenRPA}/IntegrationOrchestrator.py | 0 .../site-packages/pyOpenRPA}/JSONNormalize.py | 0 .../pyOpenRPA}/ProcessCommunicator.py | 2 +- .../Lib/site-packages/pyOpenRPA}/Robot.py | 2 +- .../site-packages/pyOpenRPA}/ValueVerify.py | 0 .../Lib/site-packages/pyOpenRPA}/Window.py | 0 .../Lib/site-packages/pyOpenRPA/__init__.py | 15 + .../Reports/ReportRobotGUIRun_2019_11_08.log | 1 + .../INSTALLER | 0 .../pyOpenRPA-1.0.15.dist-info/METADATA | 80 ++ .../pyOpenRPA-1.0.15.dist-info/RECORD | 23 + .../WHEEL | 0 .../pyOpenRPA-1.0.15.dist-info/top_level.txt | 1 + .../pyOpenRPA-1.0.7.dist-info/METADATA | 23 - .../pyOpenRPA-1.0.7.dist-info/RECORD | 5 - .../Lib/site-packages/pyOpenRPA/Clipboard.py | 22 + .../Lib/site-packages/pyOpenRPA/GUI.py | 1210 +++++++++++++++++ .../pyOpenRPA/IntegrationOrchestrator.py | 46 + .../site-packages/pyOpenRPA/JSONNormalize.py | 83 ++ .../pyOpenRPA/ProcessCommunicator.py | 143 ++ .../Lib/site-packages/pyOpenRPA/Robot.py | 158 +++ .../site-packages/pyOpenRPA/ValueVerify.py | 21 + .../Lib/site-packages/pyOpenRPA/Window.py | 13 + .../Lib/site-packages/pyOpenRPA/__init__.py | 15 + .../Reports/ReportRobotGUIRun_2019_11_08.log | 1 + Robot/CreateUploadInstall_x64_x32.cmd | 12 +- .../SeleniumRun/Python_64_Debug.cmd | 0 .../SeleniumRun/Python_64_Script_Run.cmd | 0 .../{Test => Examples}/SeleniumRun/Script.py | 0 .../SeleniumRun/ScriptSeleniumInit.py | 0 Robot/PIPUpgrade.cmd | 4 + Robot/Version.py | 16 +- Robot/__init__.py | 17 - Robot/build/lib/pyOpenRPA/Clipboard.py | 22 + Robot/build/lib/pyOpenRPA/GUI.py | 1210 +++++++++++++++++ .../lib/pyOpenRPA/IntegrationOrchestrator.py | 46 + Robot/build/lib/pyOpenRPA/JSONNormalize.py | 83 ++ .../lib/pyOpenRPA/ProcessCommunicator.py | 143 ++ Robot/build/lib/pyOpenRPA/Robot.py | 158 +++ Robot/build/lib/pyOpenRPA/ValueVerify.py | 21 + Robot/build/lib/pyOpenRPA/Window.py | 13 + Robot/build/lib/pyOpenRPA/__init__.py | 15 + Robot/dist/pyOpenRPA-1.0.15-py3-none-any.whl | Bin 0 -> 24825 bytes Robot/dist/pyOpenRPA-1.0.15.tar.gz | Bin 0 -> 22828 bytes Robot/pyOpenRPA.egg-info/PKG-INFO | 72 + Robot/pyOpenRPA.egg-info/SOURCES.txt | 16 + .../pyOpenRPA.egg-info/dependency_links.txt | 0 .../pyOpenRPA.egg-info/not-zip-safe | 0 Robot/pyOpenRPA.egg-info/requires.txt | 6 + Robot/pyOpenRPA.egg-info/top_level.txt | 1 + Robot/pyOpenRPA/Clipboard.py | 22 + Robot/pyOpenRPA/GUI.py | 1210 +++++++++++++++++ Robot/pyOpenRPA/IntegrationOrchestrator.py | 46 + Robot/pyOpenRPA/JSONNormalize.py | 83 ++ Robot/pyOpenRPA/ProcessCommunicator.py | 143 ++ Robot/{ => pyOpenRPA}/README.md | 0 Robot/pyOpenRPA/Robot.py | 158 +++ Robot/pyOpenRPA/ValueVerify.py | 21 + Robot/pyOpenRPA/Window.py | 13 + Robot/pyOpenRPA/__init__.py | 15 + Robot/setup.py | 6 +- Robot/test.json | 1 - v1.0.8 => v1.0.15 | 0 72 files changed, 5487 insertions(+), 86 deletions(-) rename Resources/WPy32-3720/python-3.7.2/Lib/site-packages/{pyOpenRPA-1.0.7.dist-info => pyOpenRPA-1.0.15.dist-info}/INSTALLER (100%) create mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA create mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD rename Resources/WPy32-3720/python-3.7.2/Lib/site-packages/{pyOpenRPA-1.0.7.dist-info => pyOpenRPA-1.0.15.dist-info}/WHEEL (100%) create mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt delete mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA delete mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/Clipboard.py (100%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/GUI.py (99%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/IntegrationOrchestrator.py (100%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/JSONNormalize.py (100%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/ProcessCommunicator.py (99%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/Robot.py (99%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/ValueVerify.py (100%) rename {Robot => Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA}/Window.py (100%) create mode 100644 Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/__init__.py create mode 100644 Resources/WPy32-3720/python-3.7.2/Reports/ReportRobotGUIRun_2019_11_08.log rename Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/{pyOpenRPA-1.0.7.dist-info => pyOpenRPA-1.0.15.dist-info}/INSTALLER (100%) create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD rename Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/{pyOpenRPA-1.0.7.dist-info => pyOpenRPA-1.0.15.dist-info}/WHEEL (100%) create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt delete mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA delete mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Clipboard.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/GUI.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/JSONNormalize.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Robot.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ValueVerify.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Window.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/__init__.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/Reports/ReportRobotGUIRun_2019_11_08.log rename Robot/{Test => Examples}/SeleniumRun/Python_64_Debug.cmd (100%) rename Robot/{Test => Examples}/SeleniumRun/Python_64_Script_Run.cmd (100%) rename Robot/{Test => Examples}/SeleniumRun/Script.py (100%) rename Robot/{Test => Examples}/SeleniumRun/ScriptSeleniumInit.py (100%) create mode 100644 Robot/PIPUpgrade.cmd delete mode 100644 Robot/__init__.py create mode 100644 Robot/build/lib/pyOpenRPA/Clipboard.py create mode 100644 Robot/build/lib/pyOpenRPA/GUI.py create mode 100644 Robot/build/lib/pyOpenRPA/IntegrationOrchestrator.py create mode 100644 Robot/build/lib/pyOpenRPA/JSONNormalize.py create mode 100644 Robot/build/lib/pyOpenRPA/ProcessCommunicator.py create mode 100644 Robot/build/lib/pyOpenRPA/Robot.py create mode 100644 Robot/build/lib/pyOpenRPA/ValueVerify.py create mode 100644 Robot/build/lib/pyOpenRPA/Window.py create mode 100644 Robot/build/lib/pyOpenRPA/__init__.py create mode 100644 Robot/dist/pyOpenRPA-1.0.15-py3-none-any.whl create mode 100644 Robot/dist/pyOpenRPA-1.0.15.tar.gz create mode 100644 Robot/pyOpenRPA.egg-info/PKG-INFO create mode 100644 Robot/pyOpenRPA.egg-info/SOURCES.txt rename Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt => Robot/pyOpenRPA.egg-info/dependency_links.txt (100%) rename Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt => Robot/pyOpenRPA.egg-info/not-zip-safe (100%) create mode 100644 Robot/pyOpenRPA.egg-info/requires.txt create mode 100644 Robot/pyOpenRPA.egg-info/top_level.txt create mode 100644 Robot/pyOpenRPA/Clipboard.py create mode 100644 Robot/pyOpenRPA/GUI.py create mode 100644 Robot/pyOpenRPA/IntegrationOrchestrator.py create mode 100644 Robot/pyOpenRPA/JSONNormalize.py create mode 100644 Robot/pyOpenRPA/ProcessCommunicator.py rename Robot/{ => pyOpenRPA}/README.md (100%) create mode 100644 Robot/pyOpenRPA/Robot.py create mode 100644 Robot/pyOpenRPA/ValueVerify.py create mode 100644 Robot/pyOpenRPA/Window.py create mode 100644 Robot/pyOpenRPA/__init__.py delete mode 100644 Robot/test.json rename v1.0.8 => v1.0.15 (100%) diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/INSTALLER b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/INSTALLER similarity index 100% rename from Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/INSTALLER rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/INSTALLER diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA new file mode 100644 index 00000000..b41a9476 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA @@ -0,0 +1,80 @@ +Metadata-Version: 2.1 +Name: pyOpenRPA +Version: 1.0.15 +Summary: First open source RPA platform for business +Home-page: https://gitlab.com/UnicodeLabs/OpenRPA +Author: Ivan Maslov +Author-email: Ivan.Maslov@unicodelabs.ru +License: MIT +Keywords: OpenRPA RPA Robot Automation Robotization +Platform: UNKNOWN +Classifier: Development Status :: 3 - Alpha +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Description-Content-Type: text/markdown +Requires-Dist: pywinauto (>=0.6.6) +Requires-Dist: WMI (>=1.4.9) +Requires-Dist: pillow (>=6.0.0) +Requires-Dist: keyboard (>=0.13.3) +Requires-Dist: pyautogui (>=0.9.44) +Requires-Dist: pywin32 (>=224) + +# OpenRPA +First open source RPA platform for business is released! + +# How to run +Studio +Double click to Studio\StudioRun_32.cmd or Studio\StudioRun_64.cmd + +# Robot how to debug +Robot\PythonDebug_64.cmd +import Robot +Robot.ActivityRun( + { + ModuleName: <"GUI"|..., str>, + ActivityName: , + ArgumentList: [, ...] - optional, + ArgumentDict: {:, ...} - optional + } +) + +# Robot example script: +Robot\Examples\GetFolderList\Python_32_Script_Run.cmd + +# Python 32 bit +Resources\WPy32-3720\python-3.7.2\python.exe + +# Python 64 bit +Resources\WPy64-3720\python-3.7.2.amd64\python.exe + +# Module GUI activity List: +############################ +Новая версия +############################ +Получить СЃРїРёСЃРѕРє элементов, который удовлетворяет условиям через расширенный движок РїРѕРёСЃРєР° +[ + { + "index":<Позиция элемента РІ родительском объекте>, + "depth_start" - глубина, СЃ которой начинается РїРѕРёСЃРє (РїРѕ умолчанию 1) + "depth_end" - глубина, РґРѕ которой ведется РїРѕРёСЃРє (РїРѕ умолчанию 1) + "class_name" - наименование класса, который требуется искать + "title" - наименование заголовка + "rich_text" - наименование rich_text + } +] + + +# Open RPA Wiki +- [Home](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/home) +- [04. Desktop app access (win32 & ui automation)](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/04.-Desktop-app-access-(win32-&-ui-automation)) + +#Dependencies +* Python 3 x32 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] +* Python 3 x64 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] +* pywinauto (Windows GUI automation) +* Semantic UI CSS framework +* JsRender by https://www.jsviews.com (switch to Handlebars) +* Handlebars + +Created by Unicode Labs (Ivan Maslov) + diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD new file mode 100644 index 00000000..07ef7d0a --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD @@ -0,0 +1,23 @@ +pyOpenRPA-1.0.15.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pyOpenRPA-1.0.15.dist-info/METADATA,sha256=OVAh2YFn_pseeM5KBSk8G19yTsO-jBMZDY8i9S8saJs,3510 +pyOpenRPA-1.0.15.dist-info/RECORD,, +pyOpenRPA-1.0.15.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97 +pyOpenRPA-1.0.15.dist-info/top_level.txt,sha256=RPzwQXgYBRo_m5L3ZLs6Voh8aEkMeT29Xsul1w1qE0g,10 +pyOpenRPA/Clipboard.py,sha256=q76X8L21zJwcwdoJJNPeCEwAV30xS6ylHP1WwvtxoWI,722 +pyOpenRPA/GUI.py,sha256=SzbCIZMAf53vEM5cwY55itseM5gcMSowF7POo2KwyeM,68726 +pyOpenRPA/IntegrationOrchestrator.py,sha256=T1g1jJM7_JMTSVP50DTM5WHrMh1w8wovvcBXl1nEokU,2656 +pyOpenRPA/JSONNormalize.py,sha256=aIuVzuZDazhxkCOzoOjfhHVz66mp2FWdfPv5E7KWF5Y,3890 +pyOpenRPA/ProcessCommunicator.py,sha256=eT_NgoAQBUxSu9alCGxuauuussrP50HDoiBA9JcUkOc,8114 +pyOpenRPA/Robot.py,sha256=a_xiI4SuSpGFAfnSrvhL8qeuFVHrBUEGOFwelRWIvTA,9407 +pyOpenRPA/ValueVerify.py,sha256=ObskxU4fOMoCGw74_nzYt6-a5jjrAckb3sdBLYyhYxY,777 +pyOpenRPA/Window.py,sha256=UJl-sg4RvvJ35aG9jZOzqGVwE15XK7qPHqoOBD13xFk,431 +pyOpenRPA/__init__.py,sha256=wJLBEdKdtm0jeWLL-KyBrTN2vfDK75uF0qGpoGQHQgI,372 +pyOpenRPA/__pycache__/Clipboard.cpython-37.pyc,, +pyOpenRPA/__pycache__/GUI.cpython-37.pyc,, +pyOpenRPA/__pycache__/IntegrationOrchestrator.cpython-37.pyc,, +pyOpenRPA/__pycache__/JSONNormalize.cpython-37.pyc,, +pyOpenRPA/__pycache__/ProcessCommunicator.cpython-37.pyc,, +pyOpenRPA/__pycache__/Robot.cpython-37.pyc,, +pyOpenRPA/__pycache__/ValueVerify.cpython-37.pyc,, +pyOpenRPA/__pycache__/Window.cpython-37.pyc,, +pyOpenRPA/__pycache__/__init__.cpython-37.pyc,, diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/WHEEL b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/WHEEL similarity index 100% rename from Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/WHEEL rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/WHEEL diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt new file mode 100644 index 00000000..4170df85 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt @@ -0,0 +1 @@ +pyOpenRPA diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA deleted file mode 100644 index 377d259f..00000000 --- a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA +++ /dev/null @@ -1,23 +0,0 @@ -Metadata-Version: 2.1 -Name: pyOpenRPA -Version: 1.0.7 -Summary: First open source RPA platform for business -Home-page: https://gitlab.com/UnicodeLabs/OpenRPA -Author: Ivan Maslov -Author-email: Ivan.Maslov@unicodelabs.ru -License: MIT -Keywords: OpenRPA RPA Robot Automation Robotization -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3.7 -Requires-Dist: pywinauto (>=0.6.6) -Requires-Dist: WMI (>=1.4.9) -Requires-Dist: pillow (>=6.0.0) -Requires-Dist: keyboard (>=0.13.3) -Requires-Dist: pyautogui (>=0.9.44) -Requires-Dist: pywin32 (>=224) - -UNKNOWN - - diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD deleted file mode 100644 index 1560492d..00000000 --- a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD +++ /dev/null @@ -1,5 +0,0 @@ -pyOpenRPA-1.0.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pyOpenRPA-1.0.7.dist-info/METADATA,sha256=_rN4UG95FENZbFjRiTmaCf31KPJa8XKkFoFbDR3vgaI,654 -pyOpenRPA-1.0.7.dist-info/RECORD,, -pyOpenRPA-1.0.7.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97 -pyOpenRPA-1.0.7.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 diff --git a/Robot/Clipboard.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Clipboard.py similarity index 100% rename from Robot/Clipboard.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Clipboard.py diff --git a/Robot/GUI.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/GUI.py similarity index 99% rename from Robot/GUI.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/GUI.py index a572260d..05b139a5 100644 --- a/Robot/GUI.py +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/GUI.py @@ -12,8 +12,8 @@ import win32api import win32clipboard import time import traceback -import ProcessCommunicator -import JSONNormalize +from . import ProcessCommunicator +from . import JSONNormalize from threading import Timer import datetime import logging diff --git a/Robot/IntegrationOrchestrator.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py similarity index 100% rename from Robot/IntegrationOrchestrator.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py diff --git a/Robot/JSONNormalize.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/JSONNormalize.py similarity index 100% rename from Robot/JSONNormalize.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/JSONNormalize.py diff --git a/Robot/ProcessCommunicator.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py similarity index 99% rename from Robot/ProcessCommunicator.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py index 582a1f9a..2ab4ceaf 100644 --- a/Robot/ProcessCommunicator.py +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py @@ -3,7 +3,7 @@ import subprocess import zlib import sys import os -import JSONNormalize +from . import JSONNormalize import pdb ############################################ ####Межпроцессное взаимодействие diff --git a/Robot/Robot.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Robot.py similarity index 99% rename from Robot/Robot.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Robot.py index c3f6c5d8..da848224 100644 --- a/Robot/Robot.py +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Robot.py @@ -3,7 +3,7 @@ import json import subprocess import zlib import os -import ProcessCommunicator +from . import ProcessCommunicator import importlib import traceback import logging diff --git a/Robot/ValueVerify.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/ValueVerify.py similarity index 100% rename from Robot/ValueVerify.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/ValueVerify.py diff --git a/Robot/Window.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Window.py similarity index 100% rename from Robot/Window.py rename to Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/Window.py diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/__init__.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/__init__.py new file mode 100644 index 00000000..f69e75bb --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA/__init__.py @@ -0,0 +1,15 @@ +r""" + +The OpenRPA package (from UnicodeLabs) + +""" +__version__ = 'v1.0.15' +__all__ = [ + 'GUI','Clipboard','IntegrationOrchestrator','Window', 'ProcessCommunicator' +] +__author__ = 'Ivan Maslov ' +from . import GUI +from . import Clipboard +from . import IntegrationOrchestrator +from . import Window +from . import ProcessCommunicator \ No newline at end of file diff --git a/Resources/WPy32-3720/python-3.7.2/Reports/ReportRobotGUIRun_2019_11_08.log b/Resources/WPy32-3720/python-3.7.2/Reports/ReportRobotGUIRun_2019_11_08.log new file mode 100644 index 00000000..3b2761e0 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Reports/ReportRobotGUIRun_2019_11_08.log @@ -0,0 +1 @@ +2019-11-08 19:47:24,560 - RobotLogger - INFO - Robot/GUI: Debug mode, x32 diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/INSTALLER b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/INSTALLER similarity index 100% rename from Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/INSTALLER rename to Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/INSTALLER diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA new file mode 100644 index 00000000..b41a9476 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/METADATA @@ -0,0 +1,80 @@ +Metadata-Version: 2.1 +Name: pyOpenRPA +Version: 1.0.15 +Summary: First open source RPA platform for business +Home-page: https://gitlab.com/UnicodeLabs/OpenRPA +Author: Ivan Maslov +Author-email: Ivan.Maslov@unicodelabs.ru +License: MIT +Keywords: OpenRPA RPA Robot Automation Robotization +Platform: UNKNOWN +Classifier: Development Status :: 3 - Alpha +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Description-Content-Type: text/markdown +Requires-Dist: pywinauto (>=0.6.6) +Requires-Dist: WMI (>=1.4.9) +Requires-Dist: pillow (>=6.0.0) +Requires-Dist: keyboard (>=0.13.3) +Requires-Dist: pyautogui (>=0.9.44) +Requires-Dist: pywin32 (>=224) + +# OpenRPA +First open source RPA platform for business is released! + +# How to run +Studio +Double click to Studio\StudioRun_32.cmd or Studio\StudioRun_64.cmd + +# Robot how to debug +Robot\PythonDebug_64.cmd +import Robot +Robot.ActivityRun( + { + ModuleName: <"GUI"|..., str>, + ActivityName: , + ArgumentList: [, ...] - optional, + ArgumentDict: {:, ...} - optional + } +) + +# Robot example script: +Robot\Examples\GetFolderList\Python_32_Script_Run.cmd + +# Python 32 bit +Resources\WPy32-3720\python-3.7.2\python.exe + +# Python 64 bit +Resources\WPy64-3720\python-3.7.2.amd64\python.exe + +# Module GUI activity List: +############################ +Новая версия +############################ +Получить СЃРїРёСЃРѕРє элементов, который удовлетворяет условиям через расширенный движок РїРѕРёСЃРєР° +[ + { + "index":<Позиция элемента РІ родительском объекте>, + "depth_start" - глубина, СЃ которой начинается РїРѕРёСЃРє (РїРѕ умолчанию 1) + "depth_end" - глубина, РґРѕ которой ведется РїРѕРёСЃРє (РїРѕ умолчанию 1) + "class_name" - наименование класса, который требуется искать + "title" - наименование заголовка + "rich_text" - наименование rich_text + } +] + + +# Open RPA Wiki +- [Home](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/home) +- [04. Desktop app access (win32 & ui automation)](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/04.-Desktop-app-access-(win32-&-ui-automation)) + +#Dependencies +* Python 3 x32 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] +* Python 3 x64 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] +* pywinauto (Windows GUI automation) +* Semantic UI CSS framework +* JsRender by https://www.jsviews.com (switch to Handlebars) +* Handlebars + +Created by Unicode Labs (Ivan Maslov) + diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD new file mode 100644 index 00000000..07ef7d0a --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/RECORD @@ -0,0 +1,23 @@ +pyOpenRPA-1.0.15.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pyOpenRPA-1.0.15.dist-info/METADATA,sha256=OVAh2YFn_pseeM5KBSk8G19yTsO-jBMZDY8i9S8saJs,3510 +pyOpenRPA-1.0.15.dist-info/RECORD,, +pyOpenRPA-1.0.15.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97 +pyOpenRPA-1.0.15.dist-info/top_level.txt,sha256=RPzwQXgYBRo_m5L3ZLs6Voh8aEkMeT29Xsul1w1qE0g,10 +pyOpenRPA/Clipboard.py,sha256=q76X8L21zJwcwdoJJNPeCEwAV30xS6ylHP1WwvtxoWI,722 +pyOpenRPA/GUI.py,sha256=SzbCIZMAf53vEM5cwY55itseM5gcMSowF7POo2KwyeM,68726 +pyOpenRPA/IntegrationOrchestrator.py,sha256=T1g1jJM7_JMTSVP50DTM5WHrMh1w8wovvcBXl1nEokU,2656 +pyOpenRPA/JSONNormalize.py,sha256=aIuVzuZDazhxkCOzoOjfhHVz66mp2FWdfPv5E7KWF5Y,3890 +pyOpenRPA/ProcessCommunicator.py,sha256=eT_NgoAQBUxSu9alCGxuauuussrP50HDoiBA9JcUkOc,8114 +pyOpenRPA/Robot.py,sha256=a_xiI4SuSpGFAfnSrvhL8qeuFVHrBUEGOFwelRWIvTA,9407 +pyOpenRPA/ValueVerify.py,sha256=ObskxU4fOMoCGw74_nzYt6-a5jjrAckb3sdBLYyhYxY,777 +pyOpenRPA/Window.py,sha256=UJl-sg4RvvJ35aG9jZOzqGVwE15XK7qPHqoOBD13xFk,431 +pyOpenRPA/__init__.py,sha256=wJLBEdKdtm0jeWLL-KyBrTN2vfDK75uF0qGpoGQHQgI,372 +pyOpenRPA/__pycache__/Clipboard.cpython-37.pyc,, +pyOpenRPA/__pycache__/GUI.cpython-37.pyc,, +pyOpenRPA/__pycache__/IntegrationOrchestrator.cpython-37.pyc,, +pyOpenRPA/__pycache__/JSONNormalize.cpython-37.pyc,, +pyOpenRPA/__pycache__/ProcessCommunicator.cpython-37.pyc,, +pyOpenRPA/__pycache__/Robot.cpython-37.pyc,, +pyOpenRPA/__pycache__/ValueVerify.cpython-37.pyc,, +pyOpenRPA/__pycache__/Window.cpython-37.pyc,, +pyOpenRPA/__pycache__/__init__.cpython-37.pyc,, diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/WHEEL b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/WHEEL similarity index 100% rename from Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/WHEEL rename to Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/WHEEL diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt new file mode 100644 index 00000000..4170df85 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.15.dist-info/top_level.txt @@ -0,0 +1 @@ +pyOpenRPA diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA deleted file mode 100644 index 377d259f..00000000 --- a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/METADATA +++ /dev/null @@ -1,23 +0,0 @@ -Metadata-Version: 2.1 -Name: pyOpenRPA -Version: 1.0.7 -Summary: First open source RPA platform for business -Home-page: https://gitlab.com/UnicodeLabs/OpenRPA -Author: Ivan Maslov -Author-email: Ivan.Maslov@unicodelabs.ru -License: MIT -Keywords: OpenRPA RPA Robot Automation Robotization -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3.7 -Requires-Dist: pywinauto (>=0.6.6) -Requires-Dist: WMI (>=1.4.9) -Requires-Dist: pillow (>=6.0.0) -Requires-Dist: keyboard (>=0.13.3) -Requires-Dist: pyautogui (>=0.9.44) -Requires-Dist: pywin32 (>=224) - -UNKNOWN - - diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD deleted file mode 100644 index 1560492d..00000000 --- a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/RECORD +++ /dev/null @@ -1,5 +0,0 @@ -pyOpenRPA-1.0.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pyOpenRPA-1.0.7.dist-info/METADATA,sha256=_rN4UG95FENZbFjRiTmaCf31KPJa8XKkFoFbDR3vgaI,654 -pyOpenRPA-1.0.7.dist-info/RECORD,, -pyOpenRPA-1.0.7.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97 -pyOpenRPA-1.0.7.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Clipboard.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Clipboard.py new file mode 100644 index 00000000..12b43186 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Clipboard.py @@ -0,0 +1,22 @@ +import win32clipboard +#################################### +#Info: Clipboard module of the Robot app (OpenRPA - Robot) +#################################### +# GUI Module - interaction with Windows clipboard + +################ +###ClipboardGet +################ +def ClipboardGet(): + win32clipboard.OpenClipboard() + lResult = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) + win32clipboard.CloseClipboard() + return lResult +################ +###ClipboardSet +################ +def ClipboardSet(inText): + win32clipboard.OpenClipboard() + win32clipboard.EmptyClipboard() + win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,inText) + win32clipboard.CloseClipboard() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/GUI.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/GUI.py new file mode 100644 index 00000000..05b139a5 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/GUI.py @@ -0,0 +1,1210 @@ +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 +#__ + +#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: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI", str>, -optional +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - 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']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 lSecsDone0: + #Условие выполнено + 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 lSecsDone0: + #Условие выполнено + 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): + 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 (lResultElementX1lChildFoundedElementX2) 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)) + + diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py new file mode 100644 index 00000000..be03032c --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/IntegrationOrchestrator.py @@ -0,0 +1,46 @@ +import requests +import grequests +#from requests import async +import json +################################### +##Orchestrator integration module (safe use when orchestrator is turned off) +################################### + +################################################################################ +#Send data to orchestrator (asynchronyous) +#Example: t=IntegrationOrchestrator.DataSend(["Storage","Robot_R01"],{"RunDateTimeString":"Test1","StepCurrentName":"Test2","StepCurrentDuration":"Test333","SafeStopSignal":True},"localhost",8081) +def DataSend(inKeyList,inValue,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictSetKeyListValue","key_list":inKeyList,"value":inValue}]} + #lAsyncList = [] + lResultItem = [grequests.post(lURL, json=lDataJSON)] + return grequests.map(lResultItem) + #lAsyncList.append(lResultItem) + #return async.map(lAsyncList) +################################################################################ +#recieve Data from orchestrator +#t=IntegrationOrchestrator.DataRecieve(["Storage","Robot_R01"],"localhost",8081) +def DataRecieve(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictGetKeyListValue","key_list":inKeyList}]} + try: + lResult = requests.post(lURL, json=lDataJSON) + lResultJSON = json.loads(lResult.text) + return lResultJSON["actionListResult"][0]["value"] + except Exception: + return None +################################################################################ +#Check if orchestrator has safe stop signal +#Example: IntegrationOrchestrator.SafeStopSignalIs(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalIs(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResult=False + lResponse=DataRecieve(inKeyList,inOrchestratorHost,inOrchestratorPort) + if lResponse is not None: + lResult = lResponse + return lResult +################################################################################ +#Reset SafeStop signal in orchestrator +#Example: t=IntegrationOrchestrator.SafeStopSignalReset(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalReset(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResponse=DataSend(inKeyList,False,inOrchestratorHost,inOrchestratorPort) + return lResponse \ No newline at end of file diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/JSONNormalize.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/JSONNormalize.py new file mode 100644 index 00000000..ef540777 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/JSONNormalize.py @@ -0,0 +1,83 @@ +import json + +#################################### +#Info: Internal JSONNormalize module of the Robot app (OpenRPA - Robot) +#################################### +# JSONNormalize Module - Prepare dict or list for JSON (delete object from the structure) + +############################### +####Нормализация под JSON (в JSON нельзя передавать классы - только null, числа, строки, словари и массивы) +############################### +#Нормализация словаря под JSON +def JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + True==True + else: + lFlagRemoveAttribute=True + #Рекурсивный вызов, если объект является словарем + if type(lItemValue) is dict: + lResult[lItemKey]=JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + lResult.append(lItemValue) + #Если является словарем - вызвать функцию нормализации словаря + if type(lItemValue) is dict: + lResult.append(JSONNormalizeDict(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=JSONNormalizeDict(inDictList) + if type(inDictList) is list: + lResult=JSONNormalizeList(inDictList) + return lResult; +def JSONNormalizeDictListStrIntBool(inDictListStrIntBool): + lResult=None + if type(inDictListStrIntBool) is dict: + lResult=JSONNormalizeDict(inDictListStrIntBool) + if type(inDictListStrIntBool) is list: + lResult=JSONNormalizeList(inDictListStrIntBool) + if type(inDictListStrIntBool) is str: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is int: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is bool: + lResult=inDictListStrIntBool + return lResult; + diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py new file mode 100644 index 00000000..2ab4ceaf --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ProcessCommunicator.py @@ -0,0 +1,143 @@ +import json +import subprocess +import zlib +import sys +import os +from . import JSONNormalize +import pdb +############################################ +####Межпроцессное взаимодействие +############################################ +#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 + JSONNormalize.JSONNormalizeDictList(inObject) + #Выполнить отправку сконвертированного объекта в JSON + ProcessParentWriteString(json.dumps(inObject)) + return +#ProcessParentReadWaitObject +def ProcessParentReadWaitObject(): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessParentReadWaitString()); + return lResult; + +#ProcessChildSendString +def ProcessChildSendString(lProcess,lString): + lByteString = zlib.compress(lString.encode("utf-8")) + #Вернуть потенциальные \n + lByteString = lByteString.replace(b'\n',b'{{n}}') + #Отправить сообщение в дочерний процесс + lProcess.stdin.write(lByteString+bytes('\n',"utf-8")) + #print(str(lByteString+bytes('\n',"utf-8"))) + lProcess.stdin.flush() + #Вернуть результат + return + +#ProcessChildReadWaitString +def ProcessChildReadWaitString(lProcess): + #Ожидаем ответ от процесса + #pdb.set_trace() + lResult = lProcess.stdout.readline() + #Обработка спец символов + #print(b'NewLine: '+lResult) + #Вернуть потенциальные \n + lResult = lResult.replace(b'{{{n}}}',b'\n') + #Вернуть \r + lResult = lResult.replace(b'{{{r}}}',b'\r') + #Вернуть \0 + lResult = lResult.replace(b'{{{0}}}',b'\0') + #Вернуть \a + lResult = lResult.replace(b'{{{a}}}',b'\a') + #Вернуть \b + lResult = lResult.replace(b'{{{b}}}',b'\b') + #Вернуть \t + lResult = lResult.replace(b'{{{t}}}',b'\t') + #Вернуть \v + lResult = lResult.replace(b'{{{v}}}',b'\v') + #Вернуть \f + lResult = lResult.replace(b'{{{f}}}',b'\f') + #print("check") + #print(str(lResult)) + lResult = zlib.decompress(lResult[0:-1]) + lResult = lResult.decode("utf-8") + #Вернуть результат + return lResult + +#ProcessChildSendObject +def ProcessChildSendObject(inProcess,inObject): + #Выполнить отправку сконвертированного объекта в JSON + ProcessChildSendString(inProcess,json.dumps(inObject)) + return +#ProcessChildReadWaitObject +def ProcessChildReadWaitObject(inProcess): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessChildReadWaitString(inProcess)); + return lResult; + +#ProcessChildSendReadWaitString +def ProcessChildSendReadWaitString(lProcess,lString): + ProcessChildSendString(lProcess,lString) + #Вернуть результат + return ProcessChildReadWaitString(lProcess) +#ProcessChildSendReadWaitObject +def ProcessChildSendReadWaitObject(inProcess,inObject): + ProcessChildSendObject(inProcess,inObject) + #Вернуть результат + return ProcessChildReadWaitString(inProcess) +#ProcessChildSendReadWaitQueue +#QueueObject - [Object,Object,...] +def ProcessChildSendReadWaitQueueObject(inProcess,inQueueObject): + lOutputObject=[] + #Циклическая отправка запросов в дочерний объект + for lItem in inQueueObject: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessChildSendObject(inProcess,lItem) + #Получить ответ от дочернего процесса + lResponseObject=ProcessChildReadWaitObject(inProcess) + #Добавить в выходной массив + lOutputObject.append(lResponseObject) + #Сформировать ответ + return lOutputObject diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Robot.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Robot.py new file mode 100644 index 00000000..da848224 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Robot.py @@ -0,0 +1,158 @@ +import pdb +import json +import subprocess +import zlib +import os +from . import ProcessCommunicator +import importlib +import traceback +import logging +import sys +import datetime +import struct +import shutil +#Создать файл логирования +# add filemode="w" to overwrite +if not os.path.exists("Reports"): + os.makedirs("Reports") +logging.basicConfig(filename="Reports\ReportRobotRun_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log", level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") + +#################################### +#Info: Main module of the Robot app (OpenRPA - Robot) +#################################### + +#Usage: +#Here you can run some activity or list of activities + +#After import this module you can use the folowing functions: +#ActivityRun(inActivitySpecificationDict): outActivityResultDict - function - run activity (function or procedure) +#ActivityRunJSON(inActivitySpecificationDictJSON): outActivityResultDictJSON +#ActivityListRun(inActivitySpecificationDictList): outActivityResultDictList - function - run list of activities (function or procedure) +#ActivityListRunJSON(inActivitySpecificationDictListJSON): outActivityResultDictListJSON + +#Naming: +#Activity - some action/list of actions +#Module - Any *.py file, which consist of area specific functions +#Argument + +#inActivitySpecificationDict: +#{ +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - required if ErrorFlag is false +#} + +#################### +#Section: Module initialization +#################### +#Start childprocess - GUI Module 32 bit +if not os.path.isfile("..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe"): + shutil.copyfile('..\\Resources\\WPy32-3720\\python-3.7.2\\python.exe',"..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe") +mProcessGUI_x32 = subprocess.Popen(['..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) +#Start childprocess - GUI Module 64 bit - uncomment after WPy64 installation +ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,{"ModuleName":"GUI","ActivityName":"Get_OSBitnessInt","ArgumentList":[],"ArgumentDict":{}}) +lOSBitness = ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32)["Result"] + +lProcessBitnessStr = str(struct.calcsize("P") * 8) +#start 64 if system support 64 +mProcessGUI_x64= None +if lOSBitness == 64: + if not os.path.isfile("..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe"): + shutil.copyfile('..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\python.exe',"..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe") + mProcessGUI_x64 = subprocess.Popen(['..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + +#################### +#Section: Activity +#################### +def ActivityRun(inActivitySpecificationDict): + #Выполнить отправку в модуль GUI, если ModuleName == "GUI" + #pdb.set_trace() + if inActivitySpecificationDict["ModuleName"] == "GUI": + if "ArgumentList" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentList"]=[] + if "ArgumentDict" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentDict"]={} + + #Если mProcessGUI_x64 не инициализирован + lFlagRun64=True + if mProcessGUI_x64 is None: + lFlagRun64=False + else: + if inActivitySpecificationDict["ActivityName"]=="UIOSelectorsSecs_WaitAppear_List": + #Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться) + lFlagRun64=True + elif inActivitySpecificationDict["ActivityName"].startswith("UIOSelector") or inActivitySpecificationDict["ActivityName"].startswith("PWASpecification"): + if len(inActivitySpecificationDict["ArgumentList"])>0: + if len(inActivitySpecificationDict["ArgumentList"][0])>0: + #Определение разрядности (32 и 64) для тех функций, где это необходимо + ###################################################### + #Выполнить проверку разрядности через UIOSelector_Get_BitnessInt + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + #pdb.set_trace() + #Внимание! Проверка разрядности специально делается на процессе 64 бита, тк процесс 32 бита зависает на 35 итерации проверки + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,{"ModuleName":"GUI","ActivityName":"UIOSelector_Get_BitnessInt","ArgumentList":[inActivitySpecificationDict["ArgumentList"][0]],"ArgumentDict":inActivitySpecificationDict["ArgumentDict"]}) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + #pdb.set_trace() + if lResponseObject["Result"]==32: + lFlagRun64=False + #Запуск 64 + #pdb.set_trace() + if lFlagRun64: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + else: + #Запуск 32 + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32) + + #Остальные модули подключать и выполнять здесь + else: + lArgumentList=[] + if "ArgumentList" in inActivitySpecificationDict: + lArgumentList=inActivitySpecificationDict["ArgumentList"] + lArgumentDict={} + if "ArgumentDict" in inActivitySpecificationDict: + lArgumentDict=inActivitySpecificationDict["ArgumentDict"] + #Подготовить результирующую структуру + lResponseObject={"ActivitySpecificationDict":inActivitySpecificationDict,"ErrorFlag":False} + try: + #Подключить модуль для вызова + lModule=importlib.import_module(inActivitySpecificationDict["ModuleName"]) + #Найти функцию + lFunction=getattr(lModule,inActivitySpecificationDict["ActivityName"]) + #Выполнить вызов и записать результат + lResponseObject["Result"]=lFunction(*lArgumentList,**lArgumentDict) + except Exception as e: + #Установить флаг ошибки и передать тело ошибки + lResponseObject["ErrorFlag"]=True + lResponseObject["ErrorMessage"]=str(e) + lResponseObject["ErrorTraceback"]=traceback.format_exc() + return lResponseObject +######################################################### +#Run list of activities +######################################################### +def ActivityListRun(inActivitySpecificationDictList): + lResult=[] + for lItem in inActivitySpecificationDictList: + lResult.append(ActivityRun(lItem)) + return lResult \ No newline at end of file diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ValueVerify.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ValueVerify.py new file mode 100644 index 00000000..ee28f066 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/ValueVerify.py @@ -0,0 +1,21 @@ +#valueVerify +#inTypeClass int, str, dict, list, NoneType +def valueVerify(inValue,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inValue is not None: + if type(inValue) == inTypeClass: + lResult = inValue + #Вернуть результат + return lResult +#valueVerifyDict +def valueVerifyDict(inDict,inKey,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inKey in inDict: + lResult = valueVerify(inDict[inKey],inTypeClass,inNotValidValue) + return lResult +#valueVerifyList +def valueVerifyList(inList,inIndex,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inIndex in inList: + lResult = valueVerify(inList[inIndex],inTypeClass,inNotValidValue) + return lResult diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Window.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Window.py new file mode 100644 index 00000000..8476bba7 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/Window.py @@ -0,0 +1,13 @@ +import ctypes +#################################### +#Info: Window module of the Robot app (OpenRPA - Robot) +#################################### +# WIndow Module - Show information dialog messages to user by the modal windows + +################ +###DialogYesNo +################ +#return 1 - Yes; 2 - No +def DialogYesNo(inTitle,inBody): + lResult = ctypes.windll.user32.MessageBoxW(0, inBody, inTitle, 1) + return lResult \ No newline at end of file diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/__init__.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/__init__.py new file mode 100644 index 00000000..f69e75bb --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA/__init__.py @@ -0,0 +1,15 @@ +r""" + +The OpenRPA package (from UnicodeLabs) + +""" +__version__ = 'v1.0.15' +__all__ = [ + 'GUI','Clipboard','IntegrationOrchestrator','Window', 'ProcessCommunicator' +] +__author__ = 'Ivan Maslov ' +from . import GUI +from . import Clipboard +from . import IntegrationOrchestrator +from . import Window +from . import ProcessCommunicator \ No newline at end of file diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Reports/ReportRobotGUIRun_2019_11_08.log b/Resources/WPy64-3720/python-3.7.2.amd64/Reports/ReportRobotGUIRun_2019_11_08.log new file mode 100644 index 00000000..e4f59293 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Reports/ReportRobotGUIRun_2019_11_08.log @@ -0,0 +1 @@ +2019-11-08 19:49:01,742 - RobotLogger - INFO - Robot/GUI: Debug mode, x64 diff --git a/Robot/CreateUploadInstall_x64_x32.cmd b/Robot/CreateUploadInstall_x64_x32.cmd index e66dc464..42ec89c5 100644 --- a/Robot/CreateUploadInstall_x64_x32.cmd +++ b/Robot/CreateUploadInstall_x64_x32.cmd @@ -1,10 +1,10 @@ cd %~dp0 +RD /S /Q "build" +RD /S /Q "dist" .\..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe setup.py sdist bdist_wheel .\..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe -m twine upload dist/* -.\..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe -m pip install --upgrade pyOpenRPA -.\..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe -m pip install --upgrade pyOpenRPA -.\..\Resources\WPy32-3720\python-3.7.2\python.exe setup.py sdist bdist_wheel -.\..\Resources\WPy32-3720\python-3.7.2\python.exe -m twine upload dist/* -.\..\Resources\WPy32-3720\python-3.7.2\python.exe -m pip install --upgrade pyOpenRPA -.\..\Resources\WPy32-3720\python-3.7.2\python.exe -m pip install --upgrade pyOpenRPA +timeout 5 +cd .. +Resources\WPy64-3720\python-3.7.2.amd64\python.exe -m pip install --upgrade pyOpenRPA +Resources\WPy32-3720\python-3.7.2\python.exe -m pip install --upgrade pyOpenRPA pause >nul \ No newline at end of file diff --git a/Robot/Test/SeleniumRun/Python_64_Debug.cmd b/Robot/Examples/SeleniumRun/Python_64_Debug.cmd similarity index 100% rename from Robot/Test/SeleniumRun/Python_64_Debug.cmd rename to Robot/Examples/SeleniumRun/Python_64_Debug.cmd diff --git a/Robot/Test/SeleniumRun/Python_64_Script_Run.cmd b/Robot/Examples/SeleniumRun/Python_64_Script_Run.cmd similarity index 100% rename from Robot/Test/SeleniumRun/Python_64_Script_Run.cmd rename to Robot/Examples/SeleniumRun/Python_64_Script_Run.cmd diff --git a/Robot/Test/SeleniumRun/Script.py b/Robot/Examples/SeleniumRun/Script.py similarity index 100% rename from Robot/Test/SeleniumRun/Script.py rename to Robot/Examples/SeleniumRun/Script.py diff --git a/Robot/Test/SeleniumRun/ScriptSeleniumInit.py b/Robot/Examples/SeleniumRun/ScriptSeleniumInit.py similarity index 100% rename from Robot/Test/SeleniumRun/ScriptSeleniumInit.py rename to Robot/Examples/SeleniumRun/ScriptSeleniumInit.py diff --git a/Robot/PIPUpgrade.cmd b/Robot/PIPUpgrade.cmd new file mode 100644 index 00000000..0dcebc5d --- /dev/null +++ b/Robot/PIPUpgrade.cmd @@ -0,0 +1,4 @@ +cd .. +Resources\WPy64-3720\python-3.7.2.amd64\python.exe -m pip install --upgrade pyOpenRPA +Resources\WPy32-3720\python-3.7.2\python.exe -m pip install --upgrade pyOpenRPA +pause >nul \ No newline at end of file diff --git a/Robot/Version.py b/Robot/Version.py index e6b9d58d..2bb6ef95 100644 --- a/Robot/Version.py +++ b/Robot/Version.py @@ -9,4 +9,18 @@ def Get(inFolderPath): lResult = None else: lResult = lResultList[0] - return lResult \ No newline at end of file + return lResult +#Update version in pyOpenRPA package +def pyOpenRPAVersionUpdate(inVarsionFolderPath,inInitFile): + #Check version is not None + lVersion = Get(inVarsionFolderPath) + if lVersion: + lInitPyFile = open(inInitFile,"r",encoding="utf8") + lInitPyFyleString = lInitPyFile.read() + lInitPyFile = open(inInitFile,"w",encoding="utf8") + lInitPyFyleVersionIndexStart = lInitPyFyleString.find("__version__") + lInitPyFyleVersionIndexEnd = lInitPyFyleString.find("\n",lInitPyFyleVersionIndexStart) + lInitPyFyleString = lInitPyFyleString[:lInitPyFyleVersionIndexStart] + f"__version__ = '{lVersion}'" + lInitPyFyleString[lInitPyFyleVersionIndexEnd:] + #print(lInitPyFyleString) + lInitPyFile.write(lInitPyFyleString) + lInitPyFile.close() \ No newline at end of file diff --git a/Robot/__init__.py b/Robot/__init__.py deleted file mode 100644 index 43b95459..00000000 --- a/Robot/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -r""" - -The OpenRPA package (from UnicodeLabs) - -""" -import Version -__version__ = Version.Get("..") -__all__ = [ - 'GUI','Clipboard','IntegrationOrchestrator','Window' -] - -__author__ = 'Ivan Maslov ' - -import GUI -import Clipboard -import IntegrationOrchestrator -import Window \ No newline at end of file diff --git a/Robot/build/lib/pyOpenRPA/Clipboard.py b/Robot/build/lib/pyOpenRPA/Clipboard.py new file mode 100644 index 00000000..12b43186 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/Clipboard.py @@ -0,0 +1,22 @@ +import win32clipboard +#################################### +#Info: Clipboard module of the Robot app (OpenRPA - Robot) +#################################### +# GUI Module - interaction with Windows clipboard + +################ +###ClipboardGet +################ +def ClipboardGet(): + win32clipboard.OpenClipboard() + lResult = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) + win32clipboard.CloseClipboard() + return lResult +################ +###ClipboardSet +################ +def ClipboardSet(inText): + win32clipboard.OpenClipboard() + win32clipboard.EmptyClipboard() + win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,inText) + win32clipboard.CloseClipboard() diff --git a/Robot/build/lib/pyOpenRPA/GUI.py b/Robot/build/lib/pyOpenRPA/GUI.py new file mode 100644 index 00000000..05b139a5 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/GUI.py @@ -0,0 +1,1210 @@ +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 +#__ + +#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: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI", str>, -optional +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - 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']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 lSecsDone0: + #Условие выполнено + 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 lSecsDone0: + #Условие выполнено + 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): + 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 (lResultElementX1lChildFoundedElementX2) 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)) + + diff --git a/Robot/build/lib/pyOpenRPA/IntegrationOrchestrator.py b/Robot/build/lib/pyOpenRPA/IntegrationOrchestrator.py new file mode 100644 index 00000000..be03032c --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/IntegrationOrchestrator.py @@ -0,0 +1,46 @@ +import requests +import grequests +#from requests import async +import json +################################### +##Orchestrator integration module (safe use when orchestrator is turned off) +################################### + +################################################################################ +#Send data to orchestrator (asynchronyous) +#Example: t=IntegrationOrchestrator.DataSend(["Storage","Robot_R01"],{"RunDateTimeString":"Test1","StepCurrentName":"Test2","StepCurrentDuration":"Test333","SafeStopSignal":True},"localhost",8081) +def DataSend(inKeyList,inValue,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictSetKeyListValue","key_list":inKeyList,"value":inValue}]} + #lAsyncList = [] + lResultItem = [grequests.post(lURL, json=lDataJSON)] + return grequests.map(lResultItem) + #lAsyncList.append(lResultItem) + #return async.map(lAsyncList) +################################################################################ +#recieve Data from orchestrator +#t=IntegrationOrchestrator.DataRecieve(["Storage","Robot_R01"],"localhost",8081) +def DataRecieve(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictGetKeyListValue","key_list":inKeyList}]} + try: + lResult = requests.post(lURL, json=lDataJSON) + lResultJSON = json.loads(lResult.text) + return lResultJSON["actionListResult"][0]["value"] + except Exception: + return None +################################################################################ +#Check if orchestrator has safe stop signal +#Example: IntegrationOrchestrator.SafeStopSignalIs(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalIs(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResult=False + lResponse=DataRecieve(inKeyList,inOrchestratorHost,inOrchestratorPort) + if lResponse is not None: + lResult = lResponse + return lResult +################################################################################ +#Reset SafeStop signal in orchestrator +#Example: t=IntegrationOrchestrator.SafeStopSignalReset(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalReset(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResponse=DataSend(inKeyList,False,inOrchestratorHost,inOrchestratorPort) + return lResponse \ No newline at end of file diff --git a/Robot/build/lib/pyOpenRPA/JSONNormalize.py b/Robot/build/lib/pyOpenRPA/JSONNormalize.py new file mode 100644 index 00000000..ef540777 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/JSONNormalize.py @@ -0,0 +1,83 @@ +import json + +#################################### +#Info: Internal JSONNormalize module of the Robot app (OpenRPA - Robot) +#################################### +# JSONNormalize Module - Prepare dict or list for JSON (delete object from the structure) + +############################### +####Нормализация под JSON (в JSON нельзя передавать классы - только null, числа, строки, словари и массивы) +############################### +#Нормализация словаря под JSON +def JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + True==True + else: + lFlagRemoveAttribute=True + #Рекурсивный вызов, если объект является словарем + if type(lItemValue) is dict: + lResult[lItemKey]=JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + lResult.append(lItemValue) + #Если является словарем - вызвать функцию нормализации словаря + if type(lItemValue) is dict: + lResult.append(JSONNormalizeDict(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=JSONNormalizeDict(inDictList) + if type(inDictList) is list: + lResult=JSONNormalizeList(inDictList) + return lResult; +def JSONNormalizeDictListStrIntBool(inDictListStrIntBool): + lResult=None + if type(inDictListStrIntBool) is dict: + lResult=JSONNormalizeDict(inDictListStrIntBool) + if type(inDictListStrIntBool) is list: + lResult=JSONNormalizeList(inDictListStrIntBool) + if type(inDictListStrIntBool) is str: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is int: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is bool: + lResult=inDictListStrIntBool + return lResult; + diff --git a/Robot/build/lib/pyOpenRPA/ProcessCommunicator.py b/Robot/build/lib/pyOpenRPA/ProcessCommunicator.py new file mode 100644 index 00000000..2ab4ceaf --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/ProcessCommunicator.py @@ -0,0 +1,143 @@ +import json +import subprocess +import zlib +import sys +import os +from . import JSONNormalize +import pdb +############################################ +####Межпроцессное взаимодействие +############################################ +#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 + JSONNormalize.JSONNormalizeDictList(inObject) + #Выполнить отправку сконвертированного объекта в JSON + ProcessParentWriteString(json.dumps(inObject)) + return +#ProcessParentReadWaitObject +def ProcessParentReadWaitObject(): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessParentReadWaitString()); + return lResult; + +#ProcessChildSendString +def ProcessChildSendString(lProcess,lString): + lByteString = zlib.compress(lString.encode("utf-8")) + #Вернуть потенциальные \n + lByteString = lByteString.replace(b'\n',b'{{n}}') + #Отправить сообщение в дочерний процесс + lProcess.stdin.write(lByteString+bytes('\n',"utf-8")) + #print(str(lByteString+bytes('\n',"utf-8"))) + lProcess.stdin.flush() + #Вернуть результат + return + +#ProcessChildReadWaitString +def ProcessChildReadWaitString(lProcess): + #Ожидаем ответ от процесса + #pdb.set_trace() + lResult = lProcess.stdout.readline() + #Обработка спец символов + #print(b'NewLine: '+lResult) + #Вернуть потенциальные \n + lResult = lResult.replace(b'{{{n}}}',b'\n') + #Вернуть \r + lResult = lResult.replace(b'{{{r}}}',b'\r') + #Вернуть \0 + lResult = lResult.replace(b'{{{0}}}',b'\0') + #Вернуть \a + lResult = lResult.replace(b'{{{a}}}',b'\a') + #Вернуть \b + lResult = lResult.replace(b'{{{b}}}',b'\b') + #Вернуть \t + lResult = lResult.replace(b'{{{t}}}',b'\t') + #Вернуть \v + lResult = lResult.replace(b'{{{v}}}',b'\v') + #Вернуть \f + lResult = lResult.replace(b'{{{f}}}',b'\f') + #print("check") + #print(str(lResult)) + lResult = zlib.decompress(lResult[0:-1]) + lResult = lResult.decode("utf-8") + #Вернуть результат + return lResult + +#ProcessChildSendObject +def ProcessChildSendObject(inProcess,inObject): + #Выполнить отправку сконвертированного объекта в JSON + ProcessChildSendString(inProcess,json.dumps(inObject)) + return +#ProcessChildReadWaitObject +def ProcessChildReadWaitObject(inProcess): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessChildReadWaitString(inProcess)); + return lResult; + +#ProcessChildSendReadWaitString +def ProcessChildSendReadWaitString(lProcess,lString): + ProcessChildSendString(lProcess,lString) + #Вернуть результат + return ProcessChildReadWaitString(lProcess) +#ProcessChildSendReadWaitObject +def ProcessChildSendReadWaitObject(inProcess,inObject): + ProcessChildSendObject(inProcess,inObject) + #Вернуть результат + return ProcessChildReadWaitString(inProcess) +#ProcessChildSendReadWaitQueue +#QueueObject - [Object,Object,...] +def ProcessChildSendReadWaitQueueObject(inProcess,inQueueObject): + lOutputObject=[] + #Циклическая отправка запросов в дочерний объект + for lItem in inQueueObject: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessChildSendObject(inProcess,lItem) + #Получить ответ от дочернего процесса + lResponseObject=ProcessChildReadWaitObject(inProcess) + #Добавить в выходной массив + lOutputObject.append(lResponseObject) + #Сформировать ответ + return lOutputObject diff --git a/Robot/build/lib/pyOpenRPA/Robot.py b/Robot/build/lib/pyOpenRPA/Robot.py new file mode 100644 index 00000000..da848224 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/Robot.py @@ -0,0 +1,158 @@ +import pdb +import json +import subprocess +import zlib +import os +from . import ProcessCommunicator +import importlib +import traceback +import logging +import sys +import datetime +import struct +import shutil +#Создать файл логирования +# add filemode="w" to overwrite +if not os.path.exists("Reports"): + os.makedirs("Reports") +logging.basicConfig(filename="Reports\ReportRobotRun_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log", level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") + +#################################### +#Info: Main module of the Robot app (OpenRPA - Robot) +#################################### + +#Usage: +#Here you can run some activity or list of activities + +#After import this module you can use the folowing functions: +#ActivityRun(inActivitySpecificationDict): outActivityResultDict - function - run activity (function or procedure) +#ActivityRunJSON(inActivitySpecificationDictJSON): outActivityResultDictJSON +#ActivityListRun(inActivitySpecificationDictList): outActivityResultDictList - function - run list of activities (function or procedure) +#ActivityListRunJSON(inActivitySpecificationDictListJSON): outActivityResultDictListJSON + +#Naming: +#Activity - some action/list of actions +#Module - Any *.py file, which consist of area specific functions +#Argument + +#inActivitySpecificationDict: +#{ +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - required if ErrorFlag is false +#} + +#################### +#Section: Module initialization +#################### +#Start childprocess - GUI Module 32 bit +if not os.path.isfile("..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe"): + shutil.copyfile('..\\Resources\\WPy32-3720\\python-3.7.2\\python.exe',"..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe") +mProcessGUI_x32 = subprocess.Popen(['..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) +#Start childprocess - GUI Module 64 bit - uncomment after WPy64 installation +ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,{"ModuleName":"GUI","ActivityName":"Get_OSBitnessInt","ArgumentList":[],"ArgumentDict":{}}) +lOSBitness = ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32)["Result"] + +lProcessBitnessStr = str(struct.calcsize("P") * 8) +#start 64 if system support 64 +mProcessGUI_x64= None +if lOSBitness == 64: + if not os.path.isfile("..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe"): + shutil.copyfile('..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\python.exe',"..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe") + mProcessGUI_x64 = subprocess.Popen(['..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + +#################### +#Section: Activity +#################### +def ActivityRun(inActivitySpecificationDict): + #Выполнить отправку в модуль GUI, если ModuleName == "GUI" + #pdb.set_trace() + if inActivitySpecificationDict["ModuleName"] == "GUI": + if "ArgumentList" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentList"]=[] + if "ArgumentDict" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentDict"]={} + + #Если mProcessGUI_x64 не инициализирован + lFlagRun64=True + if mProcessGUI_x64 is None: + lFlagRun64=False + else: + if inActivitySpecificationDict["ActivityName"]=="UIOSelectorsSecs_WaitAppear_List": + #Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться) + lFlagRun64=True + elif inActivitySpecificationDict["ActivityName"].startswith("UIOSelector") or inActivitySpecificationDict["ActivityName"].startswith("PWASpecification"): + if len(inActivitySpecificationDict["ArgumentList"])>0: + if len(inActivitySpecificationDict["ArgumentList"][0])>0: + #Определение разрядности (32 и 64) для тех функций, где это необходимо + ###################################################### + #Выполнить проверку разрядности через UIOSelector_Get_BitnessInt + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + #pdb.set_trace() + #Внимание! Проверка разрядности специально делается на процессе 64 бита, тк процесс 32 бита зависает на 35 итерации проверки + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,{"ModuleName":"GUI","ActivityName":"UIOSelector_Get_BitnessInt","ArgumentList":[inActivitySpecificationDict["ArgumentList"][0]],"ArgumentDict":inActivitySpecificationDict["ArgumentDict"]}) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + #pdb.set_trace() + if lResponseObject["Result"]==32: + lFlagRun64=False + #Запуск 64 + #pdb.set_trace() + if lFlagRun64: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + else: + #Запуск 32 + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32) + + #Остальные модули подключать и выполнять здесь + else: + lArgumentList=[] + if "ArgumentList" in inActivitySpecificationDict: + lArgumentList=inActivitySpecificationDict["ArgumentList"] + lArgumentDict={} + if "ArgumentDict" in inActivitySpecificationDict: + lArgumentDict=inActivitySpecificationDict["ArgumentDict"] + #Подготовить результирующую структуру + lResponseObject={"ActivitySpecificationDict":inActivitySpecificationDict,"ErrorFlag":False} + try: + #Подключить модуль для вызова + lModule=importlib.import_module(inActivitySpecificationDict["ModuleName"]) + #Найти функцию + lFunction=getattr(lModule,inActivitySpecificationDict["ActivityName"]) + #Выполнить вызов и записать результат + lResponseObject["Result"]=lFunction(*lArgumentList,**lArgumentDict) + except Exception as e: + #Установить флаг ошибки и передать тело ошибки + lResponseObject["ErrorFlag"]=True + lResponseObject["ErrorMessage"]=str(e) + lResponseObject["ErrorTraceback"]=traceback.format_exc() + return lResponseObject +######################################################### +#Run list of activities +######################################################### +def ActivityListRun(inActivitySpecificationDictList): + lResult=[] + for lItem in inActivitySpecificationDictList: + lResult.append(ActivityRun(lItem)) + return lResult \ No newline at end of file diff --git a/Robot/build/lib/pyOpenRPA/ValueVerify.py b/Robot/build/lib/pyOpenRPA/ValueVerify.py new file mode 100644 index 00000000..ee28f066 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/ValueVerify.py @@ -0,0 +1,21 @@ +#valueVerify +#inTypeClass int, str, dict, list, NoneType +def valueVerify(inValue,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inValue is not None: + if type(inValue) == inTypeClass: + lResult = inValue + #Вернуть результат + return lResult +#valueVerifyDict +def valueVerifyDict(inDict,inKey,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inKey in inDict: + lResult = valueVerify(inDict[inKey],inTypeClass,inNotValidValue) + return lResult +#valueVerifyList +def valueVerifyList(inList,inIndex,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inIndex in inList: + lResult = valueVerify(inList[inIndex],inTypeClass,inNotValidValue) + return lResult diff --git a/Robot/build/lib/pyOpenRPA/Window.py b/Robot/build/lib/pyOpenRPA/Window.py new file mode 100644 index 00000000..8476bba7 --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/Window.py @@ -0,0 +1,13 @@ +import ctypes +#################################### +#Info: Window module of the Robot app (OpenRPA - Robot) +#################################### +# WIndow Module - Show information dialog messages to user by the modal windows + +################ +###DialogYesNo +################ +#return 1 - Yes; 2 - No +def DialogYesNo(inTitle,inBody): + lResult = ctypes.windll.user32.MessageBoxW(0, inBody, inTitle, 1) + return lResult \ No newline at end of file diff --git a/Robot/build/lib/pyOpenRPA/__init__.py b/Robot/build/lib/pyOpenRPA/__init__.py new file mode 100644 index 00000000..f69e75bb --- /dev/null +++ b/Robot/build/lib/pyOpenRPA/__init__.py @@ -0,0 +1,15 @@ +r""" + +The OpenRPA package (from UnicodeLabs) + +""" +__version__ = 'v1.0.15' +__all__ = [ + 'GUI','Clipboard','IntegrationOrchestrator','Window', 'ProcessCommunicator' +] +__author__ = 'Ivan Maslov ' +from . import GUI +from . import Clipboard +from . import IntegrationOrchestrator +from . import Window +from . import ProcessCommunicator \ No newline at end of file diff --git a/Robot/dist/pyOpenRPA-1.0.15-py3-none-any.whl b/Robot/dist/pyOpenRPA-1.0.15-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..9289b383d79949b682f4e03cb860471b4bc1c9dd GIT binary patch literal 24825 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≫UFk2d2x=fh| z_4HAujLH#S@!iF@cq&u9xGpfD0IlRbO!ebN%&hHpV{7lSd^p6WBC9`T92=Xj`z$iv zUCsp)=LgQ8kgPAl=QoF=`4pyGYDrbLBq?{`NQ4Nf^k5r1x2?a%wMf}A!5~(09cY-0 zuM4cHs@jW5tvK^uU9lF6yc>sIH&rL*EiufZ<= z*~yw}>J-=Pg9*8M$?(`=PLwvL@l!N3(<04L^epfXZS$Xl`sc!#dTPH>O1%$$6&A1m zqtxJns?`N-N~ZHvz6Lqcu=UR1DQxWHwY@L)+u4%OyK-eF&U(2}mFqqrvZSztz;N`v zr7lz@QHpN;A`tK}Yt2(o%tpo&bwM|Ui)o)bIs&@D<&Reh?K#cNghz@^S48p;Do(Sf z!~OX;W|!G7N2b{eKVB>NN)NxKxZ#2n0Ns7)oqWl$6L-7zI5Pz(E-%{C=EMX^MI?mCByk*dYo0Cn77AaOmfg$=}P!$sUYl2e$e+6 zG&^0pN6vxXVjd=_nPI^0VPG^f=__{Ur5Y~7J;#t52C8Ph=v-#1+@az}8hMc_%pNL7ZL=H-iOwt$?OA_4hq8m7`ux0 zc7?=XD~1#fHYtSo<$o0!Y5jFMjddIbdxedb6kK!_^#+HE>1TW>t6t9^l>JK56Gi}` zUG~nD>6~3@ANU&9G?(cY2+Ot$n4PD?g0*X=5j|zAigYT+1lb%-5;152?Pz9?zNhvS zFZn?H#wQT)eg*u`bK^;gT1Xg&T`3EXu=htKixY)Y!`eXW3jr{0K|W0fi7DqcjcY3o z)PSt#;b-JqCfe0gIU z^uDkOae63LeZwNx+!_06?({ z*3bcUJh7^H4ZazpHBN%(-pj4}WoAb^w=+4eT*8>f_+Po?8%z+cDnIAUzW;QkSqX@e z#%0h6Ayt*j)7b{KbmVlRnT4;Zx_ODaj(wEE{RLm_MdWl9S}$zdF!a$(| zU#JMeU9FWaz9tf*>)X1Xtjj08O0Lj&ugdCS7N$hlWvxDHUxqwKFr3_cuqUrw!2>Wm zk@*HgXo${|AEwP~?;V+>q)S|VNGfJI&84ERd_5&BVkc)so8gCZ458?$Qajj=3+M5% zq|fPi?Xu5oLX1|{o26Y`o34&k);uYA(xi<$P8fpJe91G*_E~4g44+L({gZgw&RvfQ zX=`sunjHz#mde2DzCbHnAvAAdY2+6+`4CImK+9HpmiG1Sj~F?*Cg1O=-6VCU@c69Q z(6Wn0@+k3{NuHdgZS4VF-Zq+UiJ_7oR0b)%zpRO!?24Uj7rGzV#fg(8dSb`8?@mnc z83f%WwV^t1SCkgKb@~q~y_QkTeW(x~JMp2mEJq~6ti9)Fp1-K%Vo9Cy(eH(&_ecil zKI+{mmDmMl)O~>AO1Q2aQlX*(lY&@7wCx9(aXI%ID>s`9AH?}4;q)?8${-?WK6^iq zw?c9tBO@lcw&@WQYd_aKu0SF(0`I9>AJcGX`8>n;vxI^cU3m+}5^ZUunMB8oSMY2C zvCWxfaZZhqw_Xu^zMoKaNI{;7Q~zIasM_CvfreDjNa^@biY3BKBA z(U1u>{nwD!?g2fzMR(-Jc6vw|mw@IYkDo{k{ql2P7|l!XpFydPuK)3O{^He0eMN1(*WHF}OVxLN1+E*Ul5^KD`+lDcApO_Xlz#!CQ1*FSA;ycZK z&54xo-1L(N%y&z8If`#+@3^|U40=tn(+z+C?^Jhge+4yX5)iiC$yC^5X#H)!%)8v9 z>w~t9u{MY;xJkh^59FEy-cRcMq`O{eXpGu{Qp4THsADoDEd#T+;!UqBVH`2sq`LDs z8Xlkp_sa8ladu0^4)~k!+AHfHK}Wo1zbLA)>sfNzh;TSQzH9w_$r#nD7qiGe4%;>L z@q2acK4BHjz6hi6CX%?LB^9;GWh0ah=>oaEMcEEEk?Z!U+MBy+rrfp;C9*D24lUJ!#X@0Jw) zpHg-=i0Bv1P@GhnXLW7c?>AzywSd0Li-8Q(2KWb$XkWZ0CVmyw=@2*GNA!7X5} zwo($xVH}jil3e`8!TbRcNxyZ=o_pD(eelMX(BD}&u*9Ikw7rr~FC#s*^k;?XiSx74 zw$mM5yK>z&AHCmgwdd$JG&*_{)*tdaTrj_$t`ToUjRxKVp9yLCx_J=L8s3D+m zSm%lx#7Y*hz*goA5$!gunHD5cdw%9=|5)^Hk@p{@J_;n}wAqhf^ItT87>lkA28sV_eLQk`~*?VYtdp{Y#zQ6{(~@FJ~nruMn2 zAL6IcJcLG26!*)_IXAV6>zp#ro7C7>)a;fEm{C^!7O@)M^fA=l4<5Pcg&?bYGN1%qB$oC>|m+ zw2jub&!j?%-$PtluQU|fd(x0xF(ar<0laONonV$HS!kmq@1GUk`zUhWfK^<5#9if% zqS`vgFWe*zN-{Uhk-)E-6IP>(-51rEV`JvrKXf{kuyG!@YMMV<54}f62oer6J)~st z45t0rrwppH`s;FO20-}+1A;KD?ZcrKc^LkGxG)Rp`k%@wxhHvg<6U2|`Tr3_BNCvU zgOdVb*~UrAYiBzL+ISjA#!1quG6>EC)HMeA2slknWiy%NZsHvG^35c6oPu=J@u+(- zvM!#Qy@4` zh2HhIF(8zcSPb9Xxk+(MW$9il-))u)vN2KZWn}59xu3)#tMhx67_GFDg2+V=>yRlS zfE_Pt)^ABG_PsjhpLc;5q-^U(#fWDJb|DxX$)KWP#mfx9-(hz^B5J04XN1 z{K9Lm~Rg|nys*Tiwhhe)-R zj$g|FgJ-cW+LPQ(!YUPbV}c3UAFNb8yHqGG)QB|c2BpBN zG^5t}Z#Sk#O#2!&wc@qCm2d*2)vKet%X9pcHQcR%nVAB^rfd$WrMBXZ2Ypv^Rl{Z2 zCmPRm8mw#}c$E?OF^#+h-H`*93GMF9T0dIwjb$Mx*ysK}$N`oIpRdjP{?U5Poe3V@@%G)*?@8OrscL z~w@YyJ}vQTk_ zp`()Sijwp7Zh$Tlaf09x4vhPT+FeX(ihRf_)(1b~LiBe52wo2>pIbL9Z|Ylm9^lJQ zyYWqpI32uYppyBw+>qbaiPB&fsbr>s*#a~!?@tlG&YylB5R zxf`0;6Fd=v-x{;PsYOYIY^#C;N9P9l^Nzu&|3MTSAo*G?t19@~E6K_OOO2%aRozv{ z{tN0wiiMG1b-^~Gl#rgn=&s7$tM2tepB}9J#y_z6*6^^~`_c!(HS}j6tD5Fy3M&>$ z7wvqlozspsOKC%XE>o6=Iyae}V7d)RgjHC6>)n#aWV%vrwiEKb)&SL=j4epN8kgN7YducIT&b-*Pk?9!zu`hGvM8`s{12$0VZ!+}m>MA2EWN(bCrfi2ENl#qGx zyME7TryxU>XKOLHyu0GH=x}(PUC4P;-eW{HFn93bij^;9pA!9*Hu7D2`#=QbzTs6u z=wEDAfm^=Tn^*v{y(C|SJIZF;}X+$cXhht6lsS>e9u85awUlJYW-fZU5E))GjpSWY{Sh?-X(M{ zk##Blh#l!|z|2vFOTE0GBNrU%m7~)OwuDUVERFJqc%yc>?hU^I*Ica%*ojEaa5U(v zYD_n1b8rImneqB$KYbnkhh^$s2jkfEnkgu80?d(5S}zl41Iddv;|aVLTi{_p)mk1ix|dIL@AO)p@V5PNwuiO8Gw4dbQN=eBSFWr2<=GL2X zQ3;YWZRR=JZJ89#G6v+SB=OUHsK8i5)H)|t{1K(VY9sr_)$bjs?r9KiTN8>C98p;{ zm0q=*_7n)Q0>_!@3C7-%az$8>K=I$#-V^$uC-)(1z96rn*))JZgK4^^D^cIUV9S45 zO(~BLXVA-wRr3Yl6^I~oPjZ;xvn0I1r4IXrjcd&}D&yrZ*5g1gM*6^G=>&}_v=NId zv?H{+AkrFmkjKx4w$DR)p1Erm&GQd=ZmXuew*~WAe>3VGpupE^%ySVxI@DcLAmY~$ zE9A0IOB$l%LwPcy!?;-Y8i6_)+GChKvqmNQA&<0=?S7+ntA|lW8KoO{QXsv|)u*v? zA<_s0{M$oC-c~qpXjmZ)3QC9`13vrMtfRnj{btERu=(Nq#i`? zQOC%~I0<^x<88AP$`$DzqFK{c4Rwl1N4=fAl@b&*So_ayjLcPdhuK8kN|e?m|y+vrLoOZaN(V9MAb zC-5D{QtI;0#-pi%bZ>9^UkF{GhpZlmB+okQ;QD2_AF08xUR_>E6n4cl6mKz=6aKsK znfNr?RR%xPj7%-t_j#CYC2N}Z5dL$UcRy^yc8b(uHnphhC(&}|JbCrsu#s1QZbiU! zk3_LjX}C!z2Tf%ub^xuS%F1)<+nzuP~OM9kDhjYiD8O5XgX zlQszVBhudjsoCFgC+?4kHZdYzLnuBu>52&2lSrqY7pI#FT2, + ActivityName: , + ArgumentList: [, ...] - optional, + ArgumentDict: {:, ...} - optional + } + ) + + # Robot example script: + Robot\Examples\GetFolderList\Python_32_Script_Run.cmd + + # Python 32 bit + Resources\WPy32-3720\python-3.7.2\python.exe + + # Python 64 bit + Resources\WPy64-3720\python-3.7.2.amd64\python.exe + + # Module GUI activity List: + ############################ + Новая версия + ############################ + Получить СЃРїРёСЃРѕРє элементов, который удовлетворяет условиям через расширенный движок РїРѕРёСЃРєР° + [ + { + "index":<Позиция элемента РІ родительском объекте>, + "depth_start" - глубина, СЃ которой начинается РїРѕРёСЃРє (РїРѕ умолчанию 1) + "depth_end" - глубина, РґРѕ которой ведется РїРѕРёСЃРє (РїРѕ умолчанию 1) + "class_name" - наименование класса, который требуется искать + "title" - наименование заголовка + "rich_text" - наименование rich_text + } + ] + + + # Open RPA Wiki + - [Home](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/home) + - [04. Desktop app access (win32 & ui automation)](https://gitlab.com/UnicodeLabs/OpenRPA/wikis/04.-Desktop-app-access-(win32-&-ui-automation)) + + #Dependencies + * Python 3 x32 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] + * Python 3 x64 [psutil, pywinauto, wmi, PIL, keyboard, pyautogui, win32api (pywin32), selenium, openCV, tesseract, requests, lxml, PyMuPDF] + * pywinauto (Windows GUI automation) + * Semantic UI CSS framework + * JsRender by https://www.jsviews.com (switch to Handlebars) + * Handlebars + + Created by Unicode Labs (Ivan Maslov) +Keywords: OpenRPA RPA Robot Automation Robotization +Platform: UNKNOWN +Classifier: Development Status :: 3 - Alpha +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Description-Content-Type: text/markdown diff --git a/Robot/pyOpenRPA.egg-info/SOURCES.txt b/Robot/pyOpenRPA.egg-info/SOURCES.txt new file mode 100644 index 00000000..5df32d3a --- /dev/null +++ b/Robot/pyOpenRPA.egg-info/SOURCES.txt @@ -0,0 +1,16 @@ +setup.py +pyOpenRPA/Clipboard.py +pyOpenRPA/GUI.py +pyOpenRPA/IntegrationOrchestrator.py +pyOpenRPA/JSONNormalize.py +pyOpenRPA/ProcessCommunicator.py +pyOpenRPA/Robot.py +pyOpenRPA/ValueVerify.py +pyOpenRPA/Window.py +pyOpenRPA/__init__.py +pyOpenRPA.egg-info/PKG-INFO +pyOpenRPA.egg-info/SOURCES.txt +pyOpenRPA.egg-info/dependency_links.txt +pyOpenRPA.egg-info/not-zip-safe +pyOpenRPA.egg-info/requires.txt +pyOpenRPA.egg-info/top_level.txt \ No newline at end of file diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt b/Robot/pyOpenRPA.egg-info/dependency_links.txt similarity index 100% rename from Resources/WPy32-3720/python-3.7.2/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt rename to Robot/pyOpenRPA.egg-info/dependency_links.txt diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt b/Robot/pyOpenRPA.egg-info/not-zip-safe similarity index 100% rename from Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/pyOpenRPA-1.0.7.dist-info/top_level.txt rename to Robot/pyOpenRPA.egg-info/not-zip-safe diff --git a/Robot/pyOpenRPA.egg-info/requires.txt b/Robot/pyOpenRPA.egg-info/requires.txt new file mode 100644 index 00000000..92632ea4 --- /dev/null +++ b/Robot/pyOpenRPA.egg-info/requires.txt @@ -0,0 +1,6 @@ +pywinauto>=0.6.6 +WMI>=1.4.9 +pillow>=6.0.0 +keyboard>=0.13.3 +pyautogui>=0.9.44 +pywin32>=224 diff --git a/Robot/pyOpenRPA.egg-info/top_level.txt b/Robot/pyOpenRPA.egg-info/top_level.txt new file mode 100644 index 00000000..4170df85 --- /dev/null +++ b/Robot/pyOpenRPA.egg-info/top_level.txt @@ -0,0 +1 @@ +pyOpenRPA diff --git a/Robot/pyOpenRPA/Clipboard.py b/Robot/pyOpenRPA/Clipboard.py new file mode 100644 index 00000000..12b43186 --- /dev/null +++ b/Robot/pyOpenRPA/Clipboard.py @@ -0,0 +1,22 @@ +import win32clipboard +#################################### +#Info: Clipboard module of the Robot app (OpenRPA - Robot) +#################################### +# GUI Module - interaction with Windows clipboard + +################ +###ClipboardGet +################ +def ClipboardGet(): + win32clipboard.OpenClipboard() + lResult = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) + win32clipboard.CloseClipboard() + return lResult +################ +###ClipboardSet +################ +def ClipboardSet(inText): + win32clipboard.OpenClipboard() + win32clipboard.EmptyClipboard() + win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,inText) + win32clipboard.CloseClipboard() diff --git a/Robot/pyOpenRPA/GUI.py b/Robot/pyOpenRPA/GUI.py new file mode 100644 index 00000000..05b139a5 --- /dev/null +++ b/Robot/pyOpenRPA/GUI.py @@ -0,0 +1,1210 @@ +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 +#__ + +#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: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI", str>, -optional +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - 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']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 lSecsDone0: + #Условие выполнено + 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 lSecsDone0: + #Условие выполнено + 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): + 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 (lResultElementX1lChildFoundedElementX2) 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)) + + diff --git a/Robot/pyOpenRPA/IntegrationOrchestrator.py b/Robot/pyOpenRPA/IntegrationOrchestrator.py new file mode 100644 index 00000000..be03032c --- /dev/null +++ b/Robot/pyOpenRPA/IntegrationOrchestrator.py @@ -0,0 +1,46 @@ +import requests +import grequests +#from requests import async +import json +################################### +##Orchestrator integration module (safe use when orchestrator is turned off) +################################### + +################################################################################ +#Send data to orchestrator (asynchronyous) +#Example: t=IntegrationOrchestrator.DataSend(["Storage","Robot_R01"],{"RunDateTimeString":"Test1","StepCurrentName":"Test2","StepCurrentDuration":"Test333","SafeStopSignal":True},"localhost",8081) +def DataSend(inKeyList,inValue,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictSetKeyListValue","key_list":inKeyList,"value":inValue}]} + #lAsyncList = [] + lResultItem = [grequests.post(lURL, json=lDataJSON)] + return grequests.map(lResultItem) + #lAsyncList.append(lResultItem) + #return async.map(lAsyncList) +################################################################################ +#recieve Data from orchestrator +#t=IntegrationOrchestrator.DataRecieve(["Storage","Robot_R01"],"localhost",8081) +def DataRecieve(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lURL = f'http://{inOrchestratorHost}:{inOrchestratorPort}/ProcessingRun' + lDataJSON = {"actionList":[{"type":"AdministrationGlobalDictGetKeyListValue","key_list":inKeyList}]} + try: + lResult = requests.post(lURL, json=lDataJSON) + lResultJSON = json.loads(lResult.text) + return lResultJSON["actionListResult"][0]["value"] + except Exception: + return None +################################################################################ +#Check if orchestrator has safe stop signal +#Example: IntegrationOrchestrator.SafeStopSignalIs(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalIs(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResult=False + lResponse=DataRecieve(inKeyList,inOrchestratorHost,inOrchestratorPort) + if lResponse is not None: + lResult = lResponse + return lResult +################################################################################ +#Reset SafeStop signal in orchestrator +#Example: t=IntegrationOrchestrator.SafeStopSignalReset(["Storage","Robot_R01","SafeStopSignal"],"localhost",8081) +def SafeStopSignalReset(inKeyList,inOrchestratorHost="localhost",inOrchestratorPort=80): + lResponse=DataSend(inKeyList,False,inOrchestratorHost,inOrchestratorPort) + return lResponse \ No newline at end of file diff --git a/Robot/pyOpenRPA/JSONNormalize.py b/Robot/pyOpenRPA/JSONNormalize.py new file mode 100644 index 00000000..ef540777 --- /dev/null +++ b/Robot/pyOpenRPA/JSONNormalize.py @@ -0,0 +1,83 @@ +import json + +#################################### +#Info: Internal JSONNormalize module of the Robot app (OpenRPA - Robot) +#################################### +# JSONNormalize Module - Prepare dict or list for JSON (delete object from the structure) + +############################### +####Нормализация под JSON (в JSON нельзя передавать классы - только null, числа, строки, словари и массивы) +############################### +#Нормализация словаря под JSON +def JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + True==True + else: + lFlagRemoveAttribute=True + #Рекурсивный вызов, если объект является словарем + if type(lItemValue) is dict: + lResult[lItemKey]=JSONNormalizeDict(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 + type(lItemValue) is bool or + lItemValue is None): + lResult.append(lItemValue) + #Если является словарем - вызвать функцию нормализации словаря + if type(lItemValue) is dict: + lResult.append(JSONNormalizeDict(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=JSONNormalizeDict(inDictList) + if type(inDictList) is list: + lResult=JSONNormalizeList(inDictList) + return lResult; +def JSONNormalizeDictListStrIntBool(inDictListStrIntBool): + lResult=None + if type(inDictListStrIntBool) is dict: + lResult=JSONNormalizeDict(inDictListStrIntBool) + if type(inDictListStrIntBool) is list: + lResult=JSONNormalizeList(inDictListStrIntBool) + if type(inDictListStrIntBool) is str: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is int: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is bool: + lResult=inDictListStrIntBool + return lResult; + diff --git a/Robot/pyOpenRPA/ProcessCommunicator.py b/Robot/pyOpenRPA/ProcessCommunicator.py new file mode 100644 index 00000000..2ab4ceaf --- /dev/null +++ b/Robot/pyOpenRPA/ProcessCommunicator.py @@ -0,0 +1,143 @@ +import json +import subprocess +import zlib +import sys +import os +from . import JSONNormalize +import pdb +############################################ +####Межпроцессное взаимодействие +############################################ +#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 + JSONNormalize.JSONNormalizeDictList(inObject) + #Выполнить отправку сконвертированного объекта в JSON + ProcessParentWriteString(json.dumps(inObject)) + return +#ProcessParentReadWaitObject +def ProcessParentReadWaitObject(): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessParentReadWaitString()); + return lResult; + +#ProcessChildSendString +def ProcessChildSendString(lProcess,lString): + lByteString = zlib.compress(lString.encode("utf-8")) + #Вернуть потенциальные \n + lByteString = lByteString.replace(b'\n',b'{{n}}') + #Отправить сообщение в дочерний процесс + lProcess.stdin.write(lByteString+bytes('\n',"utf-8")) + #print(str(lByteString+bytes('\n',"utf-8"))) + lProcess.stdin.flush() + #Вернуть результат + return + +#ProcessChildReadWaitString +def ProcessChildReadWaitString(lProcess): + #Ожидаем ответ от процесса + #pdb.set_trace() + lResult = lProcess.stdout.readline() + #Обработка спец символов + #print(b'NewLine: '+lResult) + #Вернуть потенциальные \n + lResult = lResult.replace(b'{{{n}}}',b'\n') + #Вернуть \r + lResult = lResult.replace(b'{{{r}}}',b'\r') + #Вернуть \0 + lResult = lResult.replace(b'{{{0}}}',b'\0') + #Вернуть \a + lResult = lResult.replace(b'{{{a}}}',b'\a') + #Вернуть \b + lResult = lResult.replace(b'{{{b}}}',b'\b') + #Вернуть \t + lResult = lResult.replace(b'{{{t}}}',b'\t') + #Вернуть \v + lResult = lResult.replace(b'{{{v}}}',b'\v') + #Вернуть \f + lResult = lResult.replace(b'{{{f}}}',b'\f') + #print("check") + #print(str(lResult)) + lResult = zlib.decompress(lResult[0:-1]) + lResult = lResult.decode("utf-8") + #Вернуть результат + return lResult + +#ProcessChildSendObject +def ProcessChildSendObject(inProcess,inObject): + #Выполнить отправку сконвертированного объекта в JSON + ProcessChildSendString(inProcess,json.dumps(inObject)) + return +#ProcessChildReadWaitObject +def ProcessChildReadWaitObject(inProcess): + #Выполнить получение и разбор объекта + lResult=json.loads(ProcessChildReadWaitString(inProcess)); + return lResult; + +#ProcessChildSendReadWaitString +def ProcessChildSendReadWaitString(lProcess,lString): + ProcessChildSendString(lProcess,lString) + #Вернуть результат + return ProcessChildReadWaitString(lProcess) +#ProcessChildSendReadWaitObject +def ProcessChildSendReadWaitObject(inProcess,inObject): + ProcessChildSendObject(inProcess,inObject) + #Вернуть результат + return ProcessChildReadWaitString(inProcess) +#ProcessChildSendReadWaitQueue +#QueueObject - [Object,Object,...] +def ProcessChildSendReadWaitQueueObject(inProcess,inQueueObject): + lOutputObject=[] + #Циклическая отправка запросов в дочерний объект + for lItem in inQueueObject: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessChildSendObject(inProcess,lItem) + #Получить ответ от дочернего процесса + lResponseObject=ProcessChildReadWaitObject(inProcess) + #Добавить в выходной массив + lOutputObject.append(lResponseObject) + #Сформировать ответ + return lOutputObject diff --git a/Robot/README.md b/Robot/pyOpenRPA/README.md similarity index 100% rename from Robot/README.md rename to Robot/pyOpenRPA/README.md diff --git a/Robot/pyOpenRPA/Robot.py b/Robot/pyOpenRPA/Robot.py new file mode 100644 index 00000000..da848224 --- /dev/null +++ b/Robot/pyOpenRPA/Robot.py @@ -0,0 +1,158 @@ +import pdb +import json +import subprocess +import zlib +import os +from . import ProcessCommunicator +import importlib +import traceback +import logging +import sys +import datetime +import struct +import shutil +#Создать файл логирования +# add filemode="w" to overwrite +if not os.path.exists("Reports"): + os.makedirs("Reports") +logging.basicConfig(filename="Reports\ReportRobotRun_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log", level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") + +#################################### +#Info: Main module of the Robot app (OpenRPA - Robot) +#################################### + +#Usage: +#Here you can run some activity or list of activities + +#After import this module you can use the folowing functions: +#ActivityRun(inActivitySpecificationDict): outActivityResultDict - function - run activity (function or procedure) +#ActivityRunJSON(inActivitySpecificationDictJSON): outActivityResultDictJSON +#ActivityListRun(inActivitySpecificationDictList): outActivityResultDictList - function - run list of activities (function or procedure) +#ActivityListRunJSON(inActivitySpecificationDictListJSON): outActivityResultDictListJSON + +#Naming: +#Activity - some action/list of actions +#Module - Any *.py file, which consist of area specific functions +#Argument + +#inActivitySpecificationDict: +#{ +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {:, ...} - optional +#} + +#outActivityResultDict: +#{ +# ActivitySpecificationDict: { +# ModuleName: <"GUI"|..., str>, +# ActivityName: , +# ArgumentList: [, ...] - optional, +# ArgumentDict: {: , ...} - optional +# }, +# ErrorFlag: , +# ErrorMessage: - required if ErrorFlag is true, +# ErrorTraceback: - required if ErrorFlag is true, +# Result: - required if ErrorFlag is false +#} + +#################### +#Section: Module initialization +#################### +#Start childprocess - GUI Module 32 bit +if not os.path.isfile("..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe"): + shutil.copyfile('..\\Resources\\WPy32-3720\\python-3.7.2\\python.exe',"..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe") +mProcessGUI_x32 = subprocess.Popen(['..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) +#Start childprocess - GUI Module 64 bit - uncomment after WPy64 installation +ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,{"ModuleName":"GUI","ActivityName":"Get_OSBitnessInt","ArgumentList":[],"ArgumentDict":{}}) +lOSBitness = ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32)["Result"] + +lProcessBitnessStr = str(struct.calcsize("P") * 8) +#start 64 if system support 64 +mProcessGUI_x64= None +if lOSBitness == 64: + if not os.path.isfile("..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe"): + shutil.copyfile('..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\python.exe',"..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe") + mProcessGUI_x64 = subprocess.Popen(['..\\Resources\\WPy64-3720\\python-3.7.2.amd64\\OpenRPARobotGUIx64.exe','..\\Robot\\GUI.py','release'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + +#################### +#Section: Activity +#################### +def ActivityRun(inActivitySpecificationDict): + #Выполнить отправку в модуль GUI, если ModuleName == "GUI" + #pdb.set_trace() + if inActivitySpecificationDict["ModuleName"] == "GUI": + if "ArgumentList" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentList"]=[] + if "ArgumentDict" not in inActivitySpecificationDict: + inActivitySpecificationDict["ArgumentDict"]={} + + #Если mProcessGUI_x64 не инициализирован + lFlagRun64=True + if mProcessGUI_x64 is None: + lFlagRun64=False + else: + if inActivitySpecificationDict["ActivityName"]=="UIOSelectorsSecs_WaitAppear_List": + #Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться) + lFlagRun64=True + elif inActivitySpecificationDict["ActivityName"].startswith("UIOSelector") or inActivitySpecificationDict["ActivityName"].startswith("PWASpecification"): + if len(inActivitySpecificationDict["ArgumentList"])>0: + if len(inActivitySpecificationDict["ArgumentList"][0])>0: + #Определение разрядности (32 и 64) для тех функций, где это необходимо + ###################################################### + #Выполнить проверку разрядности через UIOSelector_Get_BitnessInt + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + #pdb.set_trace() + #Внимание! Проверка разрядности специально делается на процессе 64 бита, тк процесс 32 бита зависает на 35 итерации проверки + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,{"ModuleName":"GUI","ActivityName":"UIOSelector_Get_BitnessInt","ArgumentList":[inActivitySpecificationDict["ArgumentList"][0]],"ArgumentDict":inActivitySpecificationDict["ArgumentDict"]}) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + #pdb.set_trace() + if lResponseObject["Result"]==32: + lFlagRun64=False + #Запуск 64 + #pdb.set_trace() + if lFlagRun64: + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x64,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x64) + else: + #Запуск 32 + #Отправить запрос в дочерний процесс, который отвечает за работу с Windows окнами + ProcessCommunicator.ProcessChildSendObject(mProcessGUI_x32,inActivitySpecificationDict) + #Получить ответ от дочернего процесса + lResponseObject=ProcessCommunicator.ProcessChildReadWaitObject(mProcessGUI_x32) + + #Остальные модули подключать и выполнять здесь + else: + lArgumentList=[] + if "ArgumentList" in inActivitySpecificationDict: + lArgumentList=inActivitySpecificationDict["ArgumentList"] + lArgumentDict={} + if "ArgumentDict" in inActivitySpecificationDict: + lArgumentDict=inActivitySpecificationDict["ArgumentDict"] + #Подготовить результирующую структуру + lResponseObject={"ActivitySpecificationDict":inActivitySpecificationDict,"ErrorFlag":False} + try: + #Подключить модуль для вызова + lModule=importlib.import_module(inActivitySpecificationDict["ModuleName"]) + #Найти функцию + lFunction=getattr(lModule,inActivitySpecificationDict["ActivityName"]) + #Выполнить вызов и записать результат + lResponseObject["Result"]=lFunction(*lArgumentList,**lArgumentDict) + except Exception as e: + #Установить флаг ошибки и передать тело ошибки + lResponseObject["ErrorFlag"]=True + lResponseObject["ErrorMessage"]=str(e) + lResponseObject["ErrorTraceback"]=traceback.format_exc() + return lResponseObject +######################################################### +#Run list of activities +######################################################### +def ActivityListRun(inActivitySpecificationDictList): + lResult=[] + for lItem in inActivitySpecificationDictList: + lResult.append(ActivityRun(lItem)) + return lResult \ No newline at end of file diff --git a/Robot/pyOpenRPA/ValueVerify.py b/Robot/pyOpenRPA/ValueVerify.py new file mode 100644 index 00000000..ee28f066 --- /dev/null +++ b/Robot/pyOpenRPA/ValueVerify.py @@ -0,0 +1,21 @@ +#valueVerify +#inTypeClass int, str, dict, list, NoneType +def valueVerify(inValue,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inValue is not None: + if type(inValue) == inTypeClass: + lResult = inValue + #Вернуть результат + return lResult +#valueVerifyDict +def valueVerifyDict(inDict,inKey,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inKey in inDict: + lResult = valueVerify(inDict[inKey],inTypeClass,inNotValidValue) + return lResult +#valueVerifyList +def valueVerifyList(inList,inIndex,inTypeClass,inNotValidValue): + lResult = inNotValidValue + if inIndex in inList: + lResult = valueVerify(inList[inIndex],inTypeClass,inNotValidValue) + return lResult diff --git a/Robot/pyOpenRPA/Window.py b/Robot/pyOpenRPA/Window.py new file mode 100644 index 00000000..8476bba7 --- /dev/null +++ b/Robot/pyOpenRPA/Window.py @@ -0,0 +1,13 @@ +import ctypes +#################################### +#Info: Window module of the Robot app (OpenRPA - Robot) +#################################### +# WIndow Module - Show information dialog messages to user by the modal windows + +################ +###DialogYesNo +################ +#return 1 - Yes; 2 - No +def DialogYesNo(inTitle,inBody): + lResult = ctypes.windll.user32.MessageBoxW(0, inBody, inTitle, 1) + return lResult \ No newline at end of file diff --git a/Robot/pyOpenRPA/__init__.py b/Robot/pyOpenRPA/__init__.py new file mode 100644 index 00000000..f69e75bb --- /dev/null +++ b/Robot/pyOpenRPA/__init__.py @@ -0,0 +1,15 @@ +r""" + +The OpenRPA package (from UnicodeLabs) + +""" +__version__ = 'v1.0.15' +__all__ = [ + 'GUI','Clipboard','IntegrationOrchestrator','Window', 'ProcessCommunicator' +] +__author__ = 'Ivan Maslov ' +from . import GUI +from . import Clipboard +from . import IntegrationOrchestrator +from . import Window +from . import ProcessCommunicator \ No newline at end of file diff --git a/Robot/setup.py b/Robot/setup.py index ba55f7be..30266b70 100644 --- a/Robot/setup.py +++ b/Robot/setup.py @@ -1,13 +1,17 @@ from setuptools import setup, find_packages import Version def LongDescriptionRead(): - with open('README.md') as f: + with open('pyOpenRPA/README.md') as f: return f.read() +#Do pyOpenRPA package __init__ __version__ update +Version.pyOpenRPAVersionUpdate("..","pyOpenRPA/__init__.py") + setup(name='pyOpenRPA', version=Version.Get(".."), description='First open source RPA platform for business', long_description=LongDescriptionRead(), + long_description_content_type='text/markdown', classifiers=[ 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: MIT License', diff --git a/Robot/test.json b/Robot/test.json deleted file mode 100644 index 9e26dfee..00000000 --- a/Robot/test.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/v1.0.8 b/v1.0.15 similarity index 100% rename from v1.0.8 rename to v1.0.15