Python в обработчиках

Приложения Simple UI могут использовать интерпретатор Python для выполнения кода обработчиков в любом режиме. Можно писать код обработчиков используя процедуры, классы, библиотеки. Перечень библиотек и актуальная версия интерпретатора может меняться от релиза к релизу и информация об этом будет обновляться.

На текущий момент используется:
  • Версия Python - 3.11 (для сборки с OpenCV+PyTorch – версия 3.8)

Подключены библиотеки:
  • Стандартные библиотеки

  • Requests

  • Lxml

  • pony

  • Pillow

  • Jinja2

  • python-barcode

  • pymongo

  • qrcode

  • tinydb

  • tinyrecord

  • zpl

  • escpos-gen

  • pelicandbms

  • python-dateutil

Так как GooglePlay не позволяет публиковать пакеты больше 100 Мб, то периодически, в виде отдельных сборок apk публикуется сборка с:
  • Pandas

  • BeautifulSoup

  • OpenCV

  • Pytorch

  • torchvision

Взаимодействие Python-обработчиков с платформой и интерфейсом происходит через стек перменных (объект hashMap), слайсы, а также объекты напрямую – Датасеты, СУБД, файлы, прямой вызов функций платформы

hashMap (стек переменных) – объект, через который передаются переменные и т.н. комманды-переменные (ShowScreen, FinishProcess и т.д.). Это не python-словарь, как можно было бы подумать, а Java-объект HashMap<String, String>. Обращение к нему происходит через указанные методы:
  • put(ключ,значение) – поместить переменную

  • get(ключ) – получить переменную

  • remove(ключ) – удалить переменную

  • containsKey(ключ) – проверить, есть ли такой ключ в коллекции

Причем с точки зрения выполнения, сам стек (и команды в нем) анализируются в конце обработчика, то есть интерпретируется обработчик (со всему put, remove), потом система читает ключи в стеке и выполняет комманды.

Есть несколько видов python-обработчиков с разным назначением.

Тип обработчика pythonscript

Самый простой обработчик. Выполняется как excec(<код обработчика>). Нигде не хранится, по сути, это строка, запускаемая на интерпретацию. Из этого исходит тот факт, что между вызовами этого обработчика также не хранятся и переменные. Это похоже на JavaScript. Это можно было бы сравнить с запуском скриптов в разных терминалах.

Отличительной особенность данного обработчика- сам скрипт в виде base64 храниться в массиве обработчиков в method и редактируется непосредственно в окне конструкторе. Это упрощает работу с небольшими скриптами и лучше структурирует архитектуру обработчиков. Все примеры написаны с использованием этого типа обработчиков

Примечание

Simple стремиться к простоте и минимальности кода, поэтому вместо нагромождения модулей, рекомендуется ограничиваться небольшими скриптами 10-15 строк на одно событие

В обработчик заходят:
  • hashMap – стек перменных

  • process_slice – слайс процесса (на чтение)

  • screen_slice – слайс экрана (на чтение)

Также подключены:

  1. Функция get_body которые позволяет в упрощенном режиме вырезать код из текущего обработчика для запуска обработчиков (например RunEvent или для download)

Пример

def after_download_1():
   with open(hashMap.get("DownloadedFile"), encoding='utf-8-sig') as f:
       toast("Загрузили файл : "+hashMap.get("DownloadedFile"))
       hashMap.put("RestartScreen","")

postExecute = json_to_str([{"action": "run", "type": "pythonscript","method":get_body(after_download_1) }])
su.download(download_url,None,None,'goods.txt',postExecute)

2) функции **json_to_str** и **str_to_json** оберки для json.dumps(j,ensure_ascii=False, indent=4) и json.loads(s)

3) функции для работы с датасетами:
  * CreateDataSet(name,options=None)
  * GetDataSet(name)
  * DeleteDataSet(name)
  1. Функция GetDataSetData

  2. Функция SendBus для отправки в шину

Модули:
  • android – функции системы, такие как toast,beep,speak и т.д.

  • json

  • base64

Переменные:

  • _local – key-value СУБД (см. раздел Хранение)

  • _data_dir , _downloads_dir – папки конфигурации для внутреннего хранения

Общий модуль general

Также в конструкторе (в разделе Конфигурация) доступен для редактирования модуль general. Его поведение в системе несколько отличается. Он сохраняется в файл при загрузке конфигурации и доступен через import general. В этом модуле можно хранить общие функции и переменные.

Тип обработчика python

В отличии от pytonscript эти обработчики при загрузке в конфигурацию сохраняются в файл handlers_conf.py, далее из этого модуля идет вызов функций. Т.е. в method в массиве обработчиков храниться не сам текст скрипта, а имя функции, которая запускается из модуля. Обработчики такого типа (также как и pythonargs и pythonbytes) подключаются в конфигурацию в виде файлов. Должен быть как минимум файл обработчиков (это вызовы функций, объявляемых в массиве обработчиков), он пишется в конфигурацию в PyHandlers. Плюс может быть один или несколько подключаемых модулей. Они пишутся в массив PyFiles конфигурации

"PyFiles": [
           {
               "PyFileKey": "ext",
               "PyFileData": "base64-содержимое файла"
           }
       ]

Все файлы при загрузке конфигурации сохраняются в файлы в внутренней папке. Дополнительные файлы можно импортировать по ключу (они сохраняются в ту же папку с именем <key>.py

В пользовательском режиме все это сохранение происходит через конструктор. Это можно сделтаь на закладке Файлы модулей:

  • либо через обычную загрузку файлов (Загрузить основной py-файл, Добавить дополнительный Py-файл). При этом способе придется проделывать это действие при каждом изменении в файлах python

  • либо подключив GitHub(инструкция на странице конструктора). Доступно и открытый и закрытый репозиторий. Этот способ хорош тем, что также к репозиторию можно подключить ваше IDE и коммиты будут автоматически сохранться из IDE и при сохранении конфигурации. Кстати, саму конфигурацию (ui) также можно сохранять на GitHub (закладка Пользователь). Этот способ удобнее.

Когда нужно использовать python, а когда pythonscript? Приблизительно будет верным ответ, что python надо использовать в больших проектах, с более чем одним python-модулем. Для простых проектов всетаки предпочтительнее pythonscript.

Функции этого типа должны иметь определенный формат:

def foo(hashMap,_files=None,_data=None):
   hashMap.put("toast",hashMap.get("barcode")) #пример записи в стек и чтения из стека
   return hashMap

_data – для SUIP-файлов, данные, упакованные в файл

_files – устарело, но нужно указывать для совместимости

Функции, с произвольным числом аргументов pythonargs

картинка

Можно использовать функции с произвольным числом неименованных аргументов (обычно называют args). Для этого надо выбрать в настройке обработчика pythonargs

Пример кода такого обработчика:

def foo(hashMap,*args):
 param1 =  args[0]
 param2 =  args[1]
 param3 =  args[2]

 return hashMap

Функция для работы с OpenCV pythonbytes

При захвате изображения для дальнейшего анализа в OpenCV в обработчик передается изображение в виде byte-массива. Обработчики имеют следующий формат:

def detect_face(hashMap,imageBytes):
       np_data = np.asarray(imageBytes,np.uint8)
       img = cv2.imdecode(np_data,cv2.IMREAD_UNCHANGED)
  #Тут работа с изображением
       return hashMap

Функции платформы, которые работают run-time (модуль android)

Все функции доступны через команды-переменные, но часто это не совсем удобно. Например, надо вывести тост внутри долгого обработчика или обновлять уведомление внутри цикла, а не после выполнения такта обработчика (а стек-машина выполняет команды-переменные по окончанию такта). Это именно run-time на уровне системы, т.е. выполняется непосредственно в момент вызова. Все подобные функции собраны в модуль вpython- модуль android и их можно использовать так:

import android
android.toast("hello")

Интерфейсные команды:

  • toast(String toast) – вывести сообщение Андроид

  • speak(String text) – произнести текст (TTS engine)

  • listen() – запустить ожидание распознавания речи

  • vibrate() и vibrate(int duration) – вибрация и вибрация заданной длительности

  • beep()/beep(int tone)/ beep(int tone,int beep_duration,int beep_volume) – звуковой сигнал, т.ч. с возможностью выбрать тон (от 1 до 99), продолжительность и громкость (по умолчанию – 100)

  • notification(String message)/ notification (String message,String title)/ notification(String message,String title,int number) – уведомление в шторке уведомлений. Number – идентификатор уведомления, по которому к нему можно потом обратиться, чтобы либо убрать, либо перезаписать (обновить)

  • notification_progress(String message,String title,int number,int progress) – уведомление с прогресс-баром (от 0 до 100) notification_cancel(int number) – скрыть уведомление

Управляющие команды:

  • refresh_screen() запускает рефреш экрана. Предполагается, что стек будет установлен рантайм методами работы со стеком

  • refresh_screen(hashMap) - запускает рефреш и передает стек.

  • RunEvent(String handlers) – запустить массив обработчиков

  • BackgroundCommand(command) – запустить фоновую команду

  • stop() или stop(hashMap) – точка останова для отладки

Работа со стеком:

  • get_process_hashmap() – получает стек переменных экрана из любого места

  • get_cv_hashmap() – получает стек переменных ActiveCV из любого места

  • get_service_hashmap() – получает стек переменных фонового сервиса из любого места

  • process_started() – получает признак, запущен ли процесс в данный момент

  • cv_started() – получает признак, запущено ли ActiveCV в данный момент

  • put_process_hashMap(key,value) помещает значение в стек процесса

  • remove_process_hashMap(key) – удаляет значение из стека процесса

Дополгнительные Java-модули, доступные из Python-обработчиков

Вообще, правильнее будет сказать что из Python-Обработчиков доступны люые Java-классы. Но практическую ценность имеют некоторые из них.

Класс ImportUtils (from ru.travelfood.simple_ui import ImportUtils as iu):

  • getView(String tag) – получить Java-объект виджета по его переменной (и после, делать все что хочется его методами)

  • getContext() – получить контекст (контекст может понадобится в других функциях SDK)

  • getRootLayout() – получить корневой контейнер экрана

Модуль SimpleUtilites(from ru.travelfood.simple_ui import SimpleUtilites as su):

  • get_stored_file(String key) – получить абсолютный путь к медиафайлу по ключу

  • download(String url,String user,String password,String filename,String postExecute) – запустить загрузку в воркере с прогресс-баром (пример выше). По окончании выполняется обработчик postExecute

  • deleteCache() – удаляет кеш приложения