You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Wiki/RUS_Tutorial/DesktopGUI_Habr/README.md

336 lines
37 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

Специально для Хабр я начинаю серию статей-туториалов по использованию RPA платформы [OpenRPA](https://habr.com/ru/post/506766/). Буду рад получить от вас комментарии и замечания, если возникнут какие-либо вопросы. Надеюсь, что эта история не оставит вас равнодушными.
![pyOpenRPA Туториал. Управление оконными GUI приложениями](https://habrastorage.org/webt/p8/_q/mo/p8_qmot0fkekqnwyqids33fthae.png)
Ранее я писал о том, что [OpenRPA](https://habr.com/ru/post/506766/) - это первая open source RPA платформа, которая позволяет польностью избавить себя от платных RPA аналогов. И, как выяснилось в процессе, эта тема позволяет не просто снять компании с "лицензионной иглы", а еще и увеличить получаемые бизнес-эффекты от разработанных роботов. Ведь архитектура новых RPA оказалась гораздо "легче" и, как следствие, быстрее.
Благодарю всех читателей, которые проявили интерес к моей [предыдущей статье](https://habr.com/ru/post/506766/) - я очень ценю мнение других, потому что именно это позволяет мне предлагать общественности наиболее актуальные решения. Еще раз спасибо вам за проявленный интерес!
В рамках этой статьи будет приведена подробная инструкция по разработке робота, который будет манипулировать оконными GUI приложениями.
*Под оконными приложениями понимаются все виды GUI приложений, которые не визуализируются в WEB браузерах.*
<cut/>
# Ремарка. OpenRPA теперь становится pyOpenRPA
С момента опубликования [предыдущей статьи](https://habr.com/ru/post/506766/) произошли небольшие изменения в названии RPA платформы, а именно: [OpenRPA](https://gitlab.com/UnicodeLabs/OpenRPA) переименовывается в [pyOpenRPA]([https://gitlab.com/UnicodeLabs/OpenRPA]).
<cut/>
## С чем это связано?
Дело в том, что само по себе название OpenRPA является "говорящим", и "лежит на поверхности". По этой причине его выбрал я, а через некоторое время и другие. Так как концепция [pyOpenRPA]([https://gitlab.com/UnicodeLabs/OpenRPA]) заключается в абсолютно безвоздмездном использовании и открытости для всех, в этой RPA платформе нет каких-либо бюджетов. В связи с этим нет возможности и отстаивать монопольное право на использование названия (да это и не нужно). В связи с этим, было принято решение немного скорректировать название, чтобы избавить пользователей от возможной путаницы.
По поводу [OpenRPA от другой команды](https://github.com/open-rpa/openrpa): очень надеюсь, что им удастся реализовать свою идею, превзойти по всем параметрам платные RPA платформы, и сохранить свою открытось. В мире open source мы не конкуренты, а коллеги, которые трудятся в одном и том же направлении - в направлении создания полезного открытого продукта. Если говорить про их RPA платформу, то там поставлена цель создания аналога коммерческой RPA платформы с визуальным программированием в основе. Идея очень интересная и привлекательная, но крайне трудозатратная по исполнению (ведь не просто так лучшие RPA платформы постоянно дорабатываются огромными командами разработчиков, что ведет к комерциализации проекта). Желаю им удачи в достижении поставленных целей.
<cut/>
# Навигация по статьям
Так как [pyOpenRPA](https://gitlab.com/UnicodeLabs/OpenRPA) - это достаточно крупная RPA платформа: туториал будет составлен в виде серий статей, в которых будут освещаться ключевые технологии. А уже освоив эти технологии, у вас появится возможность углубиться в то, что вам нужно.
**Ниже приведу планируемый перечень статей по этой тематике (для навигации):**
- [Отказываемся от платных RPA платформ и базируемся на OpenSource (pyOpenRPA)](https://habr.com/ru/post/506766/)
- \>> pyOpenRPA туториал. Управление оконными GUI приложениями
- pyOpenRPA туториал. Управление WEB приложениями (то, что мы смотрим в Chrome, Firefox, Opera)
- pyOpenRPA туториал. Управление клавиатурой & мышью
- pyOpenRPA туториал. Распознавание графических объектов на экране
<cut/>
# Немного теории и терминов
Давайте попробуем разобраться в том, как устроены GUI приложения, и почему мы можем ими управлять.
Начнем с простого. Рассмотрим на примере классического блокнота, что видим мы, и что видит робот.
<cut/>
## Что видим мы?
![Notepad_human](https://habrastorage.org/webt/gk/q_/ky/gkq_kyyivmojkiynqossxayojck.png)
## Что видит робот?
![Notepad_pyOpenRPA](https://habrastorage.org/webt/nj/sj/zx/njsjzxdzjzdimm_ushvz-f5eqaw.png)
<cut/>
## Интерпретация
Благодаря архитектуре современных операционных систем у сторонних программ имеется программная возможность по обращению к [UI элементам - они же UIO](https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81_%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8F) сторонних GUI приложений. Эта возможность изначально разрабатывалась для того, чтобы позволить программистам проводить [регрессионное тестирование свеого софта](https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%B5_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5), но позже выяснилось, что эту возможность можно использовать и в бизнес-процессах компании.
Как мы видим на изображении выше, для робота, блокнот - это набор различных UIO. Причем не просто UIO, а UIO c **набором различных атрибутов, и набором различных действий**. И что самое главное - наш робот имеет полный доступ ко всем этим атрибутам и действиям.
> **Примеры атрибутов<br>**
> - hidden - элемент спрятан в GUI интерфейсе от глаз пользователя<br>
> - disabled - элемент недоступен для выполнения действий (нажатие, наведение мышкой и и т.д.)<br>
> **Примеры действий**
> - left click - клик левой кнопкой мыши<br>
> - right click - клик правой кнопкой мыши<br>
> - type text - ввод текста в активную область<br>
> - scroll up - пролистывание активной области вверх<br>
> - scroll down - пролистывание активной области вниз<br>
> - scroll left - пролистывание активной области влево<br>
> - scroll right - пролистывание активной области вправо<br>
Тут вы можете мне возразить, что это все ерунда, потому что в любой операционной системе реализованы алгоритмы, обеспечивающие разграничение информационных потоков, и одно приложение не может "залезть" в другое приложение. А я вам на это скажу, что это, действительно так, **но только для программного (технического) уровня**. Вся эта история с безопасностью не работает, когда речь заходит о доступе к GUI интерфейсам других приложений.
<cut/>
## Что такое UIO?
UIO - это User Interface Object (терминология pyOpenRPA). В целях обеспечения максимальной совместимости, этот экземпляр наследуется от обьектной модели, разработанной в библиотеке [pywinauto (нажми, чтобы получить список доступных функций класса)](https://pywinauto.readthedocs.io/en/latest/code/pywinauto.base_wrapper.html).
Данный подход позволяет имплементировать полезную функциональность, которая уже была успешно разработана в других бибилотеках, и дополнить ее недостающей функциональностью. В нашем случае, недостающей функциональностью является возможность динамического обращения к UIO обьектам по UIO селекторам.
<cut/>
## Правила формирования UIO селектора (UIOSelector)
UIO селектор - это список характеристических словарей (спецификаций UIO). Данные спецификации UIO содержат условия, с помощью которых библиотека pyOpenRPA определит UIO, удовлетворяющий условиям, заданным в спецификации UIO. Индекс спецификации UIO в списке UIO селектора харакетризует уровень вложенности целевого UIO.
Говоря другим языком, UIO селектор - это перечень условий, под которые может попасть 0, 1 или n UIO.
Ниже приведен перечень атрибутов - условий, которые можно использовать в спецификациях UIO:
```
[
{
"depth_start" :: [int, start from 1] :: глубина, с которой начинается поиск (по умолчанию 1),
"depth_end" :: [int, start from 1] :: глубина, до которой ведется поиск (по умолчанию 1),
"ctrl_index" || "index" :: [int, starts from 0] :: индекс UIO в списке у родительского UIO,
"title" :: [str] :: идентичное наименование атрибута *title* искомого объекта UIO,
"title_re" :: [str] :: регулярное выражение (python диалект) для отбора UIO, у которого атрибут *title* должен удовлетворять условию данного регулярного выражения,
"rich_text" :: [str] :: идентичное наименование атрибута *rich_text* искомого объекта UIO,
"rich_text_re" :: [str] :: регулярное выражение (python диалект) для отбора UIO, у которого атрибут *rich_text* должен удовлетворять условию данного регулярного выражения,
"class_name" :: [str] :: идентичное наименование атрибута *class_name* искомого объекта UIO,
"class_name_re" :: [str] :: регулярное выражение (python диалект) для отбора UIO, у которого атрибут *class_name* должен удовлетворять условию данного регулярного выражения,
"friendly_class_name" :: [str] :: идентичное наименование атрибута *friendly_class_name* искомого объекта UIO,
"friendly_class_name_re" :: [str] :: регулярное выражение (python диалект) для отбора UIO, у которого атрибут *friendly_class_name* должен удовлетворять условию данного регулярного выражения,
"control_type" :: [str] :: идентичное наименование атрибута *control_type* искомого объекта UIO,
"control_type_re" :: [str] :: регулярное выражение (python диалект) для отбора UIO, у которого атрибут *control_type* должен удовлетворять условию данного регулярного выражения,
"is_enabled" :: [bool] :: признак, что UIO доступен для выполнения действий,
"is_visible" :: [bool] :: признак, что UIO отображается на экране,
"backend" :: [str, "win32" || "uia"] :: вид способа адресации к UIO (по умолчанию "win32"). Внимание! Данный атрибут может быть указан только для первого элемента списка UIO селектора. Для остальных элементов списка данный атрибут будет проигнорирован.
},
{ ... спецификация UIO следующего уровня иерархии }
]
```
**Пример UIO селектора**
```
[
{"class_name":"CalcFrame", "backend":"win32"}, # Спецификация UIO 1-го уровня вложенности
{"title":"Hex", "depth_start":3, "depth_end": 3} # Спецификация UIO 1+3-го уровня вложенности (так как установлены атрибуты depth_start|depth_stop, определяющие глубину поиска UIO)
]
```
*PS. Перечень функций по работе с UIO селектором представлен в модуле UIDesktop (pyOpenRPA/Robot/UIDesktop.py). Именно эти функции будут использоваться при дальнейшей разработке робота.
Ознакомиться в полным перечнем функций модуля UIDesktop [можно здесь](https://gitlab.com/UnicodeLabs/OpenRPA/-/blob/master/Wiki/05.2.-Theory-&-practice.-Desktop-app-UI-access-(win32-and-UI-automation-dlls).md)*
<cut/>
# (По шагам) робот своими руками
Вот мы и добрались до самого интересного и важного раздела этого туториала - это пошаговый пример по созданию своего первого робота с использованием pyOpenRPA.
В качестве экспериментального робота поставим себе следующую цель: **Разработать робота, который будет контролировать вид интерфейса в приложении "Калькулятор"**. Если вид интерфейса будет отличаться от вида "Программист", то робот должен будет выставить данный вид в автоматическом режиме.
<cut/>
## Шаг 0. Подготовим интерпретатор Python 3 для нового робота (развернем pyOpenRPA)
В отличии от подавляющего большинства RPA платформ, в pyOpenRPA реализован другой подход подключения к проекту. Если в остальных RPA платформах необходимо писать робота на языке этой платформы (текстовый, или графический, или скриптовый язык), то в случае pyOpenRPA именно вы определяете где, как и когда нужно использовать эту библиотеку в проекте.
Доступно несколько вариантов загрузки [pyOpenRPA](https://gitlab.com/UnicodeLabs/OpenRPA):
- Вариант 1, простой. Скачать преднастроенную портативную версию с [GitLab страницы проекта](https://gitlab.com/UnicodeLabs/OpenRPA)
- Вариант 2, посложнее. Установить pyOpenRPA в свою версию интерпретатора Python 3 (pip install pyOpenRPA)
<cut/>
## Шаг 1. Создать проект робота
Для того, чтобы начать проект робота, необходимо создать папку проекта. В дальнейшем я затрону тему организации папок проектов для промышленных программных роботов. Но на текущий момент не буду заострять внимание на этом, чтобы сконцентрироваться непосредственно на основном - на логике работы с GUI окнами.
**Создадим следующую структуру проекта:**
- Папка "RobotCalc":
- Файл "RobotCalc_1.py" - скрипт робота 1, который мы пишем сейчас
- Файл "RobotCalc_1_Run_x64.cmd" - скрипт запуска робота 1
- Файл "RobotCalc_2.py" - скрипт робота 2, дополнение
- Файл "RobotCalc_2_Run_x64.cmd" - скрипт запуска робота 2
.cmd файлы в этом проекте играют важную роль - именно благодаря этим файлам мы будем иметь возможность выполнять запуск интересующего нас робота простым кликом по файлу.
**Ниже приведу пример "RobotCalc_1_Run_x64.cmd" файла (файл "RobotCalc_2_Run_x64.cmd" аналогичен):**
```
cd %~dp0
..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe "RobotCalc_1.py"
pause >nul
```
<cut/>
## Шаг 2. Запустить студию pyOpenRPA и сформировать необходимые UIO селекторы
- Открыть калькулятор (win + r > calc > enter)
**Если вы скачали преднастроенную версию pyOpenRPA с GitLab (вариант 1, простой):**
- Выполнить запуск cmd файла web студии pyOpenRPA из репозитория "pyOpenRPA\Studio\pyOpenRPA.Studio_x64.cmd"
**Если вы скачали пакет pyOpenRPA с помощью pip install pyOpenRPA (вариант 2, посложнее):**
- Выполнить запуск интерпретатора python со следующими аргументами: python -m pyOpenRPA.Studio "..\Studio\SettingsStudioExample.py", где SettingsStudioExample.py - это конфигурационный файл запуска студии pyOpenRPA. Преднастроенный шаблон этого файла можно [скачать с репозитория pyOpenRPA в GitLab](https://gitlab.com/UnicodeLabs/OpenRPA/-/blob/master/Studio/SettingsStudioExample.py)
При любом из вариантов через 5 - 15 сек. должна автоматически отобразиться web студия pyOpenRPA (см. ниже)
![pyOpenRPA_studio](https://habrastorage.org/webt/zu/pm/v7/zupmv75pwn3nseyb3mdnuzwzrwa.png)
*Внешний вид web студии pyOpenRPA*
<cut/>
- В списке открытых оконных GUI приложений найти калькулятор и активировать режим поиска UI элемента по наведению указателя мыши (Кнопка "Mouse search")
- Переключиться на калькулятор (alt + tab)
- Навести указатель мыши на тот элемент, который нам необходим для того, чтобы определить состояние интерфейса калькулятора. Выберем radio кнопку Hex. Зеленая окантовка появляется поверх калькулятора благодаря студии pyOpenRPA - именно таким образом студия сообщает нам о том, какой UI элемент она видит в калькулятора по той точке, куда наведен указатель мыши.
![calc_radiobutton](https://habrastorage.org/webt/cm/rq/qs/cmrqqsomsdbcyez1kdo6_tzmcgm.png)
*Студии pyOpenRPA подсвечивает зеленой окантовкой обнаруженный UI элемент по месту указателя мыши на калькуляторе*
- Для того, чтобы остановить процесс поиска UI элемента необходимо зажать клавишу ctrl на 2-4 секунды, после чего в WEB интерфейсе студии появится иерархния до UI элемента, который студия подсвечивала зеленой окантовкой.
<cut/>
![pyOpenRPA_studio_calc_ui_hex](https://habrastorage.org/webt/vh/ai/tl/vhaitlupj8ntvxgj10rebcgp2ay.png)
*Студия pyOpenRPA отобразила иерархию нахождения UI элемента в калькуляторе после отправки сигнала завершения поиска UI элементов (длительное нажатие ctrl)*
- Для того, чтобы убедиться в том, что элемент был обнаружен корректно, достаточно нажать кнопку "Highlight" по тому UI элементу, который интересует. Программа повторно нарисует эеленую окантовку поверх того UI элемента, который был обнаружен.
- Далее выполнить клик по UI элементу в окне иерархии в студии, после чего перейти в окно редактирования UIO селектора (UIO селектор далее будет использоваться в коде робота в Python 3)
<cut/>
![pyOpenRPA_studio_calc_ui_hex_uio](https://habrastorage.org/webt/y2/_b/ck/y2_bckz1fb7c6z9x1gpyllqv2es.png)
*Студия pyOpenRPA сформировала UIO селектор в автоматическом режиме к UI элементу калькулятора*
- В нашем примере UI элемент расположен на 4-м уровне вложенности с атрибутом title = "Hex". В автоматическом режиме pyOpenRPA формирует UIO селектор по индексам расположения в вышестоящих UI элементах. Такой подход достаточно нежелательно использовать в конечных роботах, потому что индексы расположения UI элементов могут динамически изменяться во время работы программы.
- Произведем преобразование нашего UIO селектора:
```
[{"title":"Калькулятор","class_name":"CalcFrame","backend":"win32"},{"ctrl_index":0},{"ctrl_index":6},{"ctrl_index":1}]
```
в следующий вид:
```
[{"class_name":"CalcFrame","backend":"win32"},{ "title":"Hex", "depth_start":3, "depth_end": 3}]
```
- В результате преобразований убрали лишнее условие "title":"Калькулятор" и промежуточные уровни, которые характеризовались только индексами нахождения UI элементов. Вместо этого добавили условие поиска "title":"Hex" и установили область поиска "depth_start":3, "depth_end": 3 (в нашем случае это необходимо, потому что мы убрали явные уровни вложенности). Атрибуты "class_name" накладывает условие, что надо искать прилоежние с class_name = CalcFrame, а backend указывает pyOpenRPA, какую систему поиска UI элементов использовать (win32 или uia, у каждой и них есть + и -)
- С помощью кнопки "Hightlight element" убедимся в том, что UI элемент, по-прежнему, обнаруживается студией pyOpenRPA (при нажатии на кнопку поверх UI элемента должна быть отрисована зеленая окантовка - новый UIO селектор работает корректно)
<cut/>
- Данный UIO селектор будем использовать в роботе для проверки состояния интерфейса калькулятора: если UI элемент успешно обнаруживается, то режим калькулятора установлен верный. Если UI элемент не обнаруживается, то режим калькулятора установлен неверный, и его нужно будет изменить. Для того, чтобы проверить наличие UI элемента по UIO селектору воспользуемся функцией **pyOpenRPA.Robot.UIDesktop.UIOSelector_Exist_Bool**
```
lCalcHex_IsExistBool = UIDesktop.UIOSelector_Exist_Bool(inUIOSelector=[{"class_name":"CalcFrame","backend":"win32"},{ "title":"Hex", "depth_start":3, "depth_end": 3}]) # Проверить наличие UI элемента по UIO селектору
```
- Для того, чтобы установить режим программиста, воспользуемся еще одной возможностью win32 - активация события, расположенного в меню приложения (см. ниже).
![Calc Vid Programmist](https://habrastorage.org/webt/-u/u-/ss/-uu-ss0_0dp6bg5pnw8opmbay38.png)
*Вид "Программист" в калькуляторе*
Активация элемента меню выполняется с помощью специальной функции *menu_select* у корневого UIO объекта GUI приложения.
<cut/>
- С помощью студии pyOpenRPA сформируем UIO селектор корневого объекта калькулятора
```
lUIOSelectorCalculator = [{"title":"Калькулятор","class_name":"CalcFrame","backend":"win32"}] # Сформировали UIO селектор из студии pyOpenRPA
```
- Далее запросим UIO объект по UIO селектору, после чего вызовем функцию *menu_select*, в которую передадим строковый адрес вызываемого элемента меню
```
lUIOCalculator = UIDesktop.UIOSelector_Get_UIO(inSpecificationList=lUIOSelectorCalculator) # Получить UIO экземпляр
lUIOCalculator.menu_select("&Вид -> &Программист") # Выполнить смену режима калькулятора
```
<cut/>
## Шаг 3. Консолидируем код в проекте робота
Обадая всеми необходимыми UIO селекторами и функциями, перейдем к составлению целостного скрипта робота. Ниже я приведу код RobotCalc_1.py файла, готового для запуска (python.exe "RobotCalc_1.py") c детальным описанием каждой строки.
```
from pyOpenRPA.Robot import UIDesktop # Импорт модуля, который умеет управлять UI элеметами GUI приложений
import time # Библиотека сна алгоритма
import os # Библиотека системных функций, позволит открять калькулятор, если это потребуется
lUIOSelectorCalculator = [{"title":"Калькулятор","class_name":"CalcFrame","backend":"win32"}] # Сформировали UIO селектор из студии pyOpenRPA
while True: # Вечный цикл
lUIOCalculator = UIDesktop.UIOSelector_Get_UIO(inSpecificationList=lUIOSelectorCalculator) # Получить UIO экземпляр
lCalcHex_IsExistBool = UIDesktop.UIOSelector_Exist_Bool(inUIOSelector=[{"class_name":"CalcFrame","backend":"win32"},{ "title":"Hex", "depth_start":3, "depth_end": 3}]) # Проверить наличие UI элемента по UIO селектору
if not lCalcHex_IsExistBool: # Проверить, что UI элемент отсутствует
lUIOCalculator.menu_select("&Вид -> &Программист") # Выполнить смену режима калькулятора
time.sleep(1) # Выполнить сон на 1 сек., после чего перейти на следующую итерацию
```
Внимание! При запуске робота убедитесь в том, что калькулятор находится в активном состоянии на экране вашего компьютера. Робот начнет отслеживать состояние калькулятора. Если в калькуляторе не будет установлен режим программиста, то робот в течение 1 секунды вернет его в данный режим.
<cut/>
## Дополнение. Дорабатываем робота, чтобы он еще включал калькулятор (если он выключен), раскрывал его (если он свернут)
- Для решения поставленной задачи мы уже обладаем всеми необходимыми UIO селекторами. Необходимо только определиться с функциями, которые дополнительно будем использовать.
- Для запуска калькулятора будем использовать функцию *os.system*
```
os.system("calc") # Открыть калькулятор
```
<cut/>
- Для проверки состояния окна (свернуто в трэй или развернуто) *is_minimized*
```
lUIOCalculator.is_minimized()
```
- Для восстановления свернутого окна будем использовать функцию *restore*
```
lUIOCalculator.restore() # Восстановить окно калькулятора из свернутого вида
```
<cut/>
- Итого получим следующий исходный код робота (файл RobotCalc_2.py).
```
from pyOpenRPA.Robot import UIDesktop # Импорт модуля, который умеет управлять UI элеметами GUI приложений
import time # Библиотека сна алгоритма
import os # Билбиотека системных функций, позволит открять калькулятор, если это потребуется
lUIOSelectorCalculator = [{"title":"Калькулятор","class_name":"CalcFrame","backend":"win32"}] # Сформировали UIO селектор из студии pyOpenRPA
while True: # Вечный цикл
lExistBool = UIDesktop.UIOSelector_Exist_Bool(inUIOSelector=lUIOSelectorCalculator) # Проверить наличие окна по UIO селектору
if not lExistBool: # Проверить наличие окна калькулятора
os.system("calc") # Открыть калькулятор
else: # Проверить, что окно калькулятора не свернуто
lUIOCalculator = UIDesktop.UIOSelector_Get_UIO(inSpecificationList=lUIOSelectorCalculator) # Получить UIO экземпляр
if lUIOCalculator.is_minimized(): # Проверить, что калькулятор находится в свернутом виде
lUIOCalculator.restore() # Восстановить окно калькулятора из свернутого вида
else:
lCalcHex_IsExistBool = UIDesktop.UIOSelector_Exist_Bool(inUIOSelector=[{"class_name":"CalcFrame","backend":"win32"},{ "title":"Hex", "depth_start":3, "depth_end": 3}]) # Проверить наличие UI элемента по UIO селектору
if not lCalcHex_IsExistBool: # Проверить, что UI элемент отсутствует
lUIOCalculator.menu_select("&Вид -> &Программист") # Выполнить смену режима калькулятора
time.sleep(1) # Выполнить сон на 1 сек., после чего перейти на следующую итерацию
```
*PS 1. Для сравнения: Реализация аналогичного алгоритма в другой RPA платформе с помощью инструментов визуального программирования потребует в **3-4 раза больше пространства рабочей области экрана** (в связи со спецификой визуального программирования).*
*PS 2. Перечень всех функций в модуле UIDesktop (pyOpenRPA/Robot/UIDesktop.py)
Ознакомиться в полным перечнем функций модуля UIDesktop [можно здесь](https://gitlab.com/UnicodeLabs/OpenRPA/-/blob/master/Wiki/05.2.-Theory-&-practice.-Desktop-app-UI-access-(win32-and-UI-automation-dlls).md)*
<cut/>
# Подведем итоги
Итак, мы успешно преодолели первые шаги по созданию бесплатных программных роботов. Безусловно, эта статья покрывает далеко не все области программной роботизации. В следующих статьях-туториалах мы остановимся на оставшихся "столпах" роботизированного управления (мышь, клавиатура, распознавание изображения с экрана и web манипуляции).
Надеюсь, что рассмотренные технологии, в первую очередь, помогут вам или вашей компании в достижении поставленных целей. А во вторую очередь, пусть получают выгоду и другие участники RPA рынка (да, я про вендоров платных RPA платформ, многие из которых базируются в США).
Всегда открыт к вашим комментариям, и буду рад помочь вам в решении ваших вопросов.
До скорых публикаций!