.. SimpleUI documentation master file, created by sphinx-quickstart on Sat May 16 14:23:51 2020. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Экраны =========================================== Экраны, общее ------------------ Экраны нужны для отрисовки визуальных элементов. По большей части визуальные элементы располагаются в контейнерах на экране и также есть элементы самого экрана. Процессы – это просто набор экранов. Вся логика сосредоточена в экранах – обработчики, активные элементы и т.д. Размещенные на экране элементы заполняются либо из констант либо из переменных стека либо из слайсов. Например вы разместили на экране кнопку. У нее есть Значение (Value), например «Записать» Это выглядит так в конструкторе и на форме .. image:: _static/2025_screen_1.png :scale: 65% :align: center Но можно определить значение как @btn_caption, предварительно записав в переменную btn_caption значение «Записать Документ №1» .. image:: _static/2025_screen_3.png :scale: 75% :align: center Тогда при отрисовке, будет так .. image:: _static/2025_screen_4.png :scale: 70% :align: center Когда запускается процесс, всегда запускается первый в списке экран процесса. У экрана при запуске происходит отрисовка и выполнение 2х событий друг за другом - onStart и onPostStart. Вообще видов событий у экрана всего 3: * **onStart** – событие при запуске, но до начала отрисовки. Сюда можно поместить обработчик, который заполнит переменные экрана, связанные с визуальными элементами. Надо иметь ввиду такой нюанс: если обработчик быстрый, то можно использовать тип выполнения run. А если долгий, то runasync или runprogress (иначе открытие экрана будет зависать, что неэстетично и может привлечь внимание ОС). При этом, если вы используете асинхронный обработчик, то следует понимать, что он закончится, когда экран уже отрисован, и чтобы появились визуальные элементы, которые вы заполняли в таком обработчике (например вы заполняли большой список) нужно перерисовать экран командой RefreshScreen. В общем onStart в основном используется для заполнения визуальных элементов и инициализации переменных. * **onPostStart** событие после отрисовки элементов. В некотрых случаях нужно обработься к элементу когда он уже размещен на экране * **onInput** – любые события ввода: нажатия на кнопку, чтение штрихкода, выбор меню и т.д. За редким исключением, событие ввода влечет за собой **перерисовку экрана**. Соотвественно после onInput будут пересозданы элементы и запустятся обработчики onStart и onPostStart. Как видно все многообразие событий ввода собрано в onInput, поэтому люое событие ввода имеет еще признак listener чтобы их можно было разделять. Команды управления экранами и процессами --------------------------------------------- **ShowScren, <имя экрана>** – переключает на другой экран в процессе. Т.е. если в процессе есть Экран1 и Экран2, то ShowScreen, Экран2 вызовет запуск Экран2 с соответствующими событиями onStart, onPostStart. Пример (тут и далее примеры команд пишутся для python, если у вас другие обработчики, переводите их под свой стек) .. code-block:: Python hashMap.put("ShowScreen","Экран 2"); **ShowScreen,<ИмяПроцесса>|<ИмяЭкрана>** - команда заимствования экрана из любого процесса (не обязательно текущего). Хороша тем, что работает без перезапуска процесса, в отличии от StartProcess (а перезапуск довольно затратный процесс, при котром происходит инициализация оборудования и другие длительные вещи) .. code-block:: Python hashMap.put("ShowScreen","Общие формы|Экран 2"); **ResreshScreen, без параметра** – обновление (перерисовка экрана) без вызова onStart/onPostStart. Нужна для того, чтобы перерисовать элементы из заполненных переменных. Обязательно использовать с любым асинхроном. Также, см. далее команду UpdateLayout для частичного апдейта выбранного контейнера. **RestartScreen, без параметров** – перезапуск экрана, с выполнением onStart/onPostStart **StartProcess, <имя процесса>** – запуск процесса из процесса **ShowProcessScreen, <имя процесса>** – запуск процесса из фона (когда ни один процесс не запущен) **FinishProcess, без параметра** – завершение процесса Общие функции экранов -------------------------- Таймер экрана ~~~~~~~~~~~~~~~~~~ На экране можно определить таймер, который будет периодически генерировать событие ввода (частота таймера задается в общих настройках). Данная функция была введена до того, как в платформе появились асинхронные обработчики. Большинство сценариев для которых нужен таймер гораздо более эффективно реализуются на асинхроне и системе не нужно постоянно дергать экран, более того это еще и более быстрая реакция. Поэтому прежде чем использовать таймер, рекомендуется посмотреть в сторону асинхронных вызовов экранов или сервиса событий. Скрыть верхнюю панель ~~~~~~~~~~~~~~~~~~~~~~~~ Скрывает тулбар в экране. Если предусмотрен поиск в тулбаре или меню, то лучше того не делать Нижняя панель и свойство Скрыть нижнюю панель ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ В SimpleUI ввиду совместимости на экране по умолчанию присутствует панель с кнопками Вперед (listener- пустая строка), Назад и План-факт. Это устаревший рудимент и рекомендуется отключать его галкой «Скрыть нижнюю панель» Отключить прокрутку корневого контейнера ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Все элементы экрана по умолчанию выводятся в бесконечный прокручиваемый по вертикали контейнер. Но в случае например списков (которые сами имею свою прокрутку) это создает некорректную ситуацию. Например нельзя разместить список «на весь экран» если этот экран – бесконечный. Значит нужно его ограничить(зафиксировать). Для этого существует опция Отключить прокрутку корневого контейнера. Закрывать без вопроса ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ По умолчанию при закрытии экрана/процесса кнопкой назад (системной) пользователю задается вопрос. Этой галочкой можно отключить данный диалог. Подключить обработчик клавиатуры ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ При включенном флаге, когда происходит нажатие одного из сочетаний клавиш генерируется событие listener= keyboard и в переменную keyboard возвращается считанная клавиша. Список клавиш такой (в обычном режиме): * сочетания с Ctrl * сочетания с Alt * сочетания с Shift * F1-F12 Также можно в настройках приложения установить галку **«Перехватывать все клавиши»** тогда будут возвращаться абсолютно все клавиши причем еще в виде 2х событий - нажатие и отпускание. Важно помнить - в таком режиме коды клавиш пишутся в с префиксами #up и #down - это нужно учитывать в обработчиках. Для того, чтобы понять какие коды у клавиш можно воспользоваться пунктом главного меню «Тест клавиатуры» Элементы экрана ----------------------- Далее перечисленные все актуальные элементы экрана (не упомянутые элементы оставлены для совместимости со старыми конфигурациями) * **Контейнер** – элемент разметки и контейнер для других визуальных элементов. Подробнее в разделе «Разметка экрана контейнерами» * **Штрихкод** – подключение к экрану обработчика чтения штрихкодов. Штрихкоды могут считываться через подключенное оборудование (подробнее в разделе Оборудование) либо через камеру. Если в экране подключен элемент штрихкод, то на экране появляется плавающая кнопка «Штрихкод», которая запускает считывание камерой. Если эта кнопка не нужна, ее можно скрыть в общих настройках приложения «Аппаратный сканер». Также штрихкоды могут считываться ActiveCV (описано в разделе ActiveCV) в более расширенном виде. Этот вариант (в случае с камерой) просто возвращает 1 штрикход на один запуск (нет потокового сканирования). В элменте нужно определить Переменную куда будет возвращаться штрихкод. При считывании (без разницы камерой или оборудованием) генерируется событие listener=barcode и в переменную Штрихкода пишется считанный штрихкод * **Голос** Запускается распознавание голоса. У элемента надо определить Переменную, куда будет возвращен результат. В случае успеха возникает событие listener=voice и в переменную помещается результат. Также возможно запускать распознавание из кода, см в разделе Голос и синтез речи. * **Подпись** Ввод подписи с экрана. Нужно определить переменную куда запишется base64-картинка подписи. При успешном вводе подписи генерируется событие listener=signature * **Фото камерой**, **Фото из галереи**, **Горизонтальная галерея мультимедиа** и **Галерея слайдер** Подробнее об этих элементах написано в разделе Работа с мультимедиа * **Элемент меню** Можно добавить элемент в меню опций (в тулбаре). У элемента нужно определить Значение – название пункта меню и Переменную – она будет возвращена в listener при выборе пункта меню. Причем если значение задать в виде иконки Awesome (см. пункт Awesome) то такой пункт меню будет в тулбаре * **Плавающая кнопка** Можно размещать свои кнопки поверх экрана справа снизу. У элемента нужно определить Переменную и Значение. В Значении можно либо написать имя одной из предопределенных иконок либо использовать Awesome-иконку. Предопределённые: ``"forward","backward","run","cancel","edit","picture","info","settings","plus","save","search","send","done"``. Awesome описаны в разделе Awesome-иконки ниже. Ввиду того, что иконки генерируемые, они имеют разные пропорции. Поэтому именно в Плавающей кнопке желательно использовать сдвиги по x и y. Например, вот так определяется иконка «звездочка» но с сдвигом по X- 0, а по Y - -15: #f005;0;-15. Если не использовать сдвиг по y, то звездочка съезжает вниз. * **NFC** Подключение считывания NFC. Описано в разделе NFC. Разметка контейнерами ----------------------- Высота, ширина, вес ~~~~~~~~~~~~~~~~~~~~~~~ Визуальные элементы находятся внутри контейнеров. В экране должен быть хотя бы один контейнер (корневой) в котором размещаются элементы, в т.ч. другие контейнеры. Контейнеры могут иметь **горизонтальную** и **вертикальную** ориентацию (свойство Ориентация(ключ orientation)) **Высота**("height") и **ширина**("width") контейнера может быть: * **«На всю ширину»** (match_parent) – контейнер занимает по выбранной оси все пространство. * **«По размеру элементов»** (wrap_content) – размер определяется соответствующим максимальным размером вложенных элементов * **Определенную вручную** – числовое значение в относительных экранных единицах Если мы хотим разбить экран на зоны (пополам или в других пропорциях) по вертикали, то прежде всего нужно **Отключить прокрутку корневого контейнера**. Т.е. мы работаем не в бесконечном прокручиваемом экране, а в рамках одного экрана с конечной высотой и шириной. И контейнеры, и элементы внутри них подчиняются одним и тем же правилам размещения. Если нужно распределить несколько элементов в рамках контейнера пропорционально ширине или длине, то нужно выполнить следующее (пример для распределения горизонтально, для вертикального распределения действует аналогичное правило): 1) Элементы должны быть включены в горизонтальный контейнер (type: LinearLayout, orientation:horizontal), с шириной match_parent 2) у элементов внутри должна быть ширина= 0 3) у элементов должен быть вес (ключ weight) в тех пропорциях, в которых нужно распределить элементы на экране. Например, еcли два элемента должны делить ширину пополам, то у обоих должен быть вес 1, если один должен быть в два раза шире то у него должен быть вес 2, а у другого – 1. Т.е. вес это пропорция или вес элемента в рамках контейнера по оси. .. image:: _static/2025_screen_5.png :scale: 75% :align: center Еще один вариант часто встречающейся разметки: *нужно растянуть элемент, но оставить место для элементов сверху и снизу. Например, у вас сверху несколько элементов, а снизу вы хотите разместить блок кнопок. Между ними пространство может быть пустое, либо занято списком, как вариант.* .. image:: _static/2025_screen_6.png :scale: 75% :align: center .. warning:: Важно! Если вы что то хотите растягивать по верстикали - обязательно отключите прокрутку корневого контейнера! Для этого элементы сверху имеют высоту по размеру элементов(wrap_content), снизу – тоже по размеру элементов, а элемент посередине имеет **высоту("height")=0 и вес(weight)=1** Два вышеприведенных примера тут: https://disk.yandex.ru/d/JOJh0OtV1aAdig Раздел конфигурации Контейнеры ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_7.png :scale: 70% :align: center Контейнеры используются в экранах, в макетах элементов списка, диалогах и команде SetRootLayout. Во всех этих местах можно определять контейнер непосредственно в виде JSON-строки. Но часто, например со списками и диалогами хранить контейнеры в отдельном месте (раздел конфигурации Контейнеры (в структуре конфигурации массив Layouts)), а при определении списков или диалогов, либо в setRootLayout использовать ссылку на переменную контейнера. В разделе Контейнеры действуют все те же самые принципы построения контейнеров, толкьо с одним условием – у контейнера (верхнего уровня) обязательно должна быть переменная, по которой к нему можно будет ссылаться. Конфигурация с примерами использования такого подхода: https://disk.yandex.ru/d/v9ZYd8GlMH_03w Прочие поля и свойства контейнера, методы ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Переменная.** Необязательное для контейнера поле. Может использоваться для того, чтобы обратиться к контейнеру из какой либо функции. Например есть команда-переменная **UpdateLayout, <переменная контейнера>** по которой обновиться только указанный контейнер. Т.е. это аналог RefreshScreen, но только для одного контейнера. Эту команду желательно использовать, когда на экране что то тяжелое, например ActiveCV Замена контейнера экрана ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Макет экрана, определенный из кода, можно динамически заменить в экране командой-переменной **SetRootLayout** (из onStart например). В качестве параметра к ней, можно использовать как JSON-строку с нужным контейнером, так и ссылку на контейнер через префикс "^" Общие для элементов и контейнеров свойства ----------------------------------------------- **Цвет фона** (свойство BackgroundColor) – цвет, задаваемый в виде HEX-значения. Например #89f096 **Толщина окантовки** (ключ StrokeWidth) - рисует рамку вокруг контейнера или элемента с заданной толщиной в экранных единицах **Внутренние отступы** (ключ Padding) – отступы вглубь контейнера в экранных единицах **Радиус** (ключ Радиус) – если задана Толщина окантовки, то рисуется не прямые поля а закругление. Для элемента можно назначить радиус, равный -1. Тогда закругление будет в виде полного круга. Cвойства и функции элементов экрана -------------------------------------- Все элементы имеют поле **Переменная (ключ Variable)**. Это ИД элемента. Оно выполняет несколько функций: 1) как правило при генерации событий, имя переменной пишется в listener (Например на экране несколько кнопок и при нажатии в listener пишется переменная кнопки) 2) По переменной можно обратиться к элементу, например установить видимость (см. ниже) 3) по переменной можно получить Java-объект функцией getView чтобы проделать с ним низкоуровневые манипуляции на уровне AndroidSDK **Значение (ключ Value)**. В зависимости от типа элемента – это различное значение, видимое в элементе. Для надписи – это текст надписи, для кнопки – текста кнопки, для таблицы – весь макет таблицы с данными и т.д. Значение может задаваться в виде константы, т.е. в виде просто текста в конфигурации, может быть в виде ссылки на переменную стека (через символ @) и может быть задано в виде ссылки на слайс (через символ #) **Выравнивание по горизонтали** (gravity_horizontal) – выравнивание элемента отновительно контейнера. Может принимать значения left, right, center **Размер текста (TextSize)**, **Цвет текста(TextColor)**, **текст жирный ("TextBold")**, **Наклонный ("TextItalic")** – задает параметры элементов, имеющих надписи (например Надпись): размер в виде относительного размера, цвет в виде HEX-значения, жирный и наклонный - булево **Количество знаков(NumberPrecision)** – количество знаков после запятой для полей ввода типа число. **Писать в слайс (Slice)** – для полей ввода информация будет писаться в слайс экрана/процесса в типизированном виде (без преобразования в строку ) – JSON-структуру доступную через переменные process_slice (слайс, существующий на протяжении всего процесса) и screen_slice (слайс экрана) в обработчике pythonscript Подсветка обязательных полей и ошибок заполнения ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Возможны варианты: * Галочка элемента Подсвечивать пустое. Это просто автоматическая подсветка незаполненного. Ни на что не влияет, убирается, когда происходит заполнение * Галочка Не пропускать пустое. Выдаст визуальную ошибку на поле и не даст выполнится обработчику дальше. * Проверка поля в обработчике и визуальная выдача ошибки, если поле не соответствует. Это уже проверка не просто на заполненность, а на значение самого поля. Выполняется с помощью команды **Stop_<переменная поля>**. Понятно, что и без этого достаточно вывести тост или что то подобное, но так нагляднее Управление видимостью элементов ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Для управления видимостью отдельных элементов экрана следует использовать команду **Show_<Идентификатор элемента>**, например ``hashMap.put(«Show_left»,»-1») `` , где left - переменная элемента (переменная=идентификатор). Значения могут быть: * "1" - виден * "0" - не виден, без освобождения места, * "1" - виден, с освобождением места Отключение перерисовки экрана, генерации событий, подсветка краcным и зеленым ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Можно использовать команду **noRefresh**, которая отключает перерисовку элементов - т.е. обновление экрана при выполнении обработчика. Пример: .. code-block:: Python hashMap.put("noRefresh","") Можно отключить события по полям ввода, котрые автоматически генерируют события при вводе (например Галочка, поля с отслеживание ввода символов) командой **disable_events**, без параметров. Можно использовать подсветку полей ввода бледно-зеленым и бледно-красным цветом. Для того чтобы обозначить пользователю корректный или некорректный ввод можно выделить неярким красным или зеленым цветом поля ввода списком * SetRed, <список переменных полей ввода через “;”> - выделяет список полей красным * SetGreen, <список переменных полей ввода через “;”> - выделяет список полей зеленым Фокусировка ~~~~~~~~~~~~~~~ Для поля ввода можно задать принудительную фокусировку – тогда при открытии экрана поле ввода будет активным (с курсором), командой **FocusField, <имя переменной поля>**. Можно выбрать только одно поле. Допустим у вас экран ввода штрихкода, а на следующем экране необходимо сразу Html-строки -------------- Все надписи, практических всех элементов (не только непосредственно элементы Надпись, но и прочие элементы, например текст в таблицах) можно размечать HTML-тегами. Это мощный и простой способ оживить интерфейс без усложнения лишними контейнерами, фоновыми цветами и т.д., Например можно выделить часть строки так. Это простой текст, а это жирный .. image:: _static/2025_screen_8.png :scale: 55% :align: center Вот примерный список поддерживаемых тегов: * p * ul * li * div * span * strong * b * em * cite * dfn * i * big * small * font * blockquote * tt * a * u * del * s * strike * sup * sub * h1 * h2 * h3 * h4 * h5 * h6 * img * br Awesome-иконки ------------------- .. image:: _static/2025_screen_9.png :scale: 55% :align: center В системе можно использовать растровые иконки (файлы подключаются через раздел Медиафайлы и потом используются через префикс ^). Но можно использовать набор шрифта Awesome. Это набор пиктограмм, с помощью которого можно сделать свои красивые кнопки, сделать иконки на карточках, экранах и т.д. Используется бесплатный набор из 1001 иконки (отбор по free + отбор по solid) https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free Для того чтобы использовать, надо взять на сайте Unicode -код например f6be и присвоить переменной с префиксом # – т.е. #f6be, который потом указать в заголовке кнопки или надписи. Это можно использовать в элементах экранов, диалогов и т.д. Например: * Кнопка * Список кнопок * Горизонтальный список кнопок * Надпись Упрощенная разметка полей ввода с заголовком помощью “|” -------------------------------------------------------------- Все поля ввода (кроме современного поля ввода) можно разместить вместе с заголовком в упрощённом виде. Как это было бы в стандартном исполнении? Для этого надо сделать горизонтальный контейнер и разместить в нет два элемента – Надпись (заголовок поля) и поле ввода с весами у обоих =1. Тогда мы получим поле с заголовком. Это же действие можно выполнить, если разместить просто поле ввода: .. image:: _static/2025_screen_10.png :scale: 90% :align: center Элементы контейнера ------------------------ Надпись ~~~~~~~~~~~~~~~~ Надпись(TextView) – выволится надпись из переменной или константы. Кнопка ~~~~~~~~~~~~ Кнопка(Button) – простая текстовая кнопка, но с помощью Awesome можно превратить ее в кнопку с иконкой. При нажатии генерирует событие с listener=переменная кнопки. Простые поля ввода ~~~~~~~~~~~~~~~~~~~~~~ Поле ввода строка(EditTextText), Поле ввода число(EditText EditTextNumeric), Поле ввода пароль(EditTextPass), Многострочный текст(MultilineText) – простые текстовые поля ввода соответствующих данных(регулируется фильтром на ввод). Для пароля введенный текст скрывается. Современное поле ввода ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_11.png :scale: 55% :align: center Поле ввода, в котором размещается заголовок/подсказка в зависимости от того, заполнено оно или нет. Если в поле присутствует информация, подсказка смещается в область заголовка. Таким образом достаточно размещать только один элемент, что экономит место и упрощает разработку. Значение задается в виде JSON. Обязательными является hint – подсказка. Например .. code-block:: JSON { "hint":"Логин", "default_text":"default_login" } – задает подсказку, и если поле уже должно содержать предопределенные данные, то они задаются в **default_text** (можно передавать не константу, а ссылку на переменную через @) По умолчанию – это текстовое поле, но можно задать любой тип, имеющийся на Андроиде через **input_type**. Варианты – тут: https://developer.android.com/reference/android/text/InputType Также, можно задать **counter** – счетчик введенных символов внизу и counter_max – максимальное количество символов. Ключом **events** можно подключить генерацию событий с каждым введенным символом в поле, чтобы например записывать в БД сразу же введенный текст. Значение должно быть «true» Пример определения со всеми возможными опциями: .. code-block:: JSON { "hint":"Пароль", "default_text":"default_password", "counter":"true", "counter_max":15, "input_type":145, "password":true, "events":true, } Поле ввода с событием ~~~~~~~~~~~~~~~~~~~~~~~~~~ Поле ввода с событием (EditTextAuto) – текстовое поле, при вводе каждого символа в котором генерируется событие и срабатывает обработчик Галочка ~~~~~~~~~~~~~ Галочка (CheckBox) – поле чекбокса с событием изменения(генерируется событие с listener=имя переменно). Значение пишется в переменную стека (переменная определяется в Переменной(Variable)) (и читается также из переменной, а не из значения). В поле Значение (Value) - заголовок поля. Дата ~~~~~~~~ Дата(DateField) поле выбора даты из календаря. При выборе даты в переменную даты пишется отформатированная в соответствии с региональными настройками выбранная пользователем дата, а в переменную <переменная_поля>_d пишется дата в ISO-формате. В слайсы, аналогично пишутся даты в двух форматах – представление и ISO. DatePicker, NumberPicher, TimePicker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_12.png :scale: 55% :align: center Визуальные элементы для выбора числа, даты, времени. Выбранные значения записываются в переменную. Начальная установка определяется в Значении. Формат Значения для элементов: * Пикер числа: {"min":0,"max":10,"value":"@number"}, где min/max задают диапазон, value- установленное значение * Пикер даты: {"year":@year,"month":@month,"day":"@day"} - значение пишется в виде года, месяца и дня * Пикер времени: {"hour":@hour,"minute":@minute} – часы, минуты Картинка (см. раздел «Работа с изображениями») ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Выпадающий список ~~~~~~~~~~~~~~~~~~~~~~~ Выпадающий список – простой выпадающий список. Элементы можно задать в виде строки с разделителем «;» через переменную или прямо в конструкторе. В списке всегда будет выбран первый элемент, поэтому если требуется чтобы по умолчанию было пусто , первым элементом следует определить пустую строку или что то подобное. Например "<Выберите значение>;Первый;Второй". В Переменную при этом возвращается выбранное значение. Можно указать значение по умолчанию - то значение, которое будет выбрано при открытии. Для этого нужно в стек переменных поместить переменную с именем переменной результата. Например, если переменная - res то помещаем в hashMap.put("res","Второй"). .. note:: Для выбора ссылочных объектов предназначены поля датасетов (см. Датасеты) Список кнопок вертикальный и Список кнопок горизонтальный ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Размещение нескольких кнопок с помощью списка. В значении передается список заголовков кнопок, и они размещаются в ряд вертикально или горизонтально. При нажатии генерируется событие с listener=<переменная кнопки> и также в стек помещается переменная =перменной кнопки со значением, равным заголовком кнопки. Индикатор(Gauge) ~~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_14.png :scale: 55% :align: center стрелочная диаграмма для визуального представления какого то значения В качестве значения передается JSON-строка с ключами Min(минимальное значение шкалы), Max (максимальное значение шкалы), Unit (единица измерения) и Value (текущее значение) Пример: .. code-block:: Python tmenu['gauge_tasks'] = json.dumps({"Min":0,"Max":100,"Value":q,"Unit":" "}) Диаграммы. Линейная (Line), Столбчатая (Bar) и Круговая(Pie) вывод диаграмм в контейнер ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_15.png :scale: 55% :align: center .. note:: Кстати, еще одним хорошим способом вывести диаграммы и вообще отчеты является HTML-поле + какая либо библиотека для диаграмм на python. См. HTML-поле и например пример подключения библиотеки pygal https://infostart.ru/1c/articles/1760354/ Пример Значения столбчатой диаграммы .. code-block:: JSON {"type":"Bar","Datasets":[{"name":"Оптовые продажи","values":[{"x":"2017","y":"100"},{"x":"2018","y":"210"},{"x":"2019","y":"260"}]},{"name":"Розничные продажи","values":[{"x":"2017","y":"55"},{"x":"2018","y":"40"},{"x":"2019","y":"75"}]}]} Пример Значения линейной диаграммы .. code-block:: JSON {"type":"Line","Datasets":[{"name":"Оптовые продажи","values":[{"x":"2017","y":"100"},{"x":"2018","y":"210"},{"x":"2019","y":"260"}]},{"name":"Розничные продажи","values":[{"x":"2017","y":"55"},{"x":"2018","y":"40"},{"x":"2019","y":"75"}]}]} Пример Значения круговой диаграммы .. code-block:: JSON {"type":"Pie","dataset_name":"по регионам","PieDataset":[{"value":35,"caption":"ЦФО"},{"value":20,"caption":"СЗФО"},{"value":45,"caption":"Прочие"}]} Таблица и Список карточек ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Таблица и список карточек отличаются только дизайном и форматом значения. По сути это один и тот же элемент с одними и теми же свойствами и поведением. Таблица не имеет границ строк по умолчанию, их надо задавать в контейнере если требуется. Тоже самое с чересстрочной разметкой. .. image:: _static/2025_screen_17.png :scale: 75% :align: center В Списке карточек элементы оформлены в виде карточек .. image:: _static/2025_screen_16.png :scale: 100% :align: center Существует несколько подходов к определению значения этих списков, поэтому укажем универсальный формат Для списка карточек переменная будет иметь структуру: .. code-block:: JSON { "customcards":{ "options":ОПЦИИ, "layout": КОНТЕЙНЕР, "cardsdata": ДАТАСЕТ } } Для таблицы: .. code-block:: JSON { "customtable":{ "options":ОПЦИИ, "layout": КОНТЕЙНЕР, "tabledata": ДАТАСЕТ } } Где: **ОПЦИИ** (необязательный) – объект с различным набором опций: * **search_enabled**,булево – поиск в тулбаре. Для объектов без механизма датасетов – это поиск по вхождению строки по всем полям поиска. Для датасетов поиск настраиваемый см. Датасеты. * **save_position**, булево – сохранение поизиции при перерисовке. Например при обновлении строки. Работает только для списков с конечным датасетом. При использовании пополнения порциями в настоящий момент не работает. * **override_search**, булево – переопределить поиск в тулбаре. Будет срока поиска, но при вводе будет осуществляться не поиск, а генерация события ввода с переменной "SearchString" и listener=Search. Фильтрацию списка в данном случае осуществляет сам разработчик * **search_submit** – поиск/генерация событий соуществляется не после каждого введенного символа, а после подтверждения ввода на клавиатуре * **colorizing** использование выборочной раскраски элементов (см. далее) * **horizontal** – горизонтальное направление списка **КОНТЕЙНЕР** – либо JSON с текстом контейнера (текст можно скопировать, например из конструктора) непосредственно (что неудобно и громоздко), либо ссылка на контейнер определенный в конфигурации через символ ^. Например, "layout": "^item1" (см. пример ниже) **ДАТАСЕТ** – либо JSON-массив, либо ссылка на датасет(механизм датасеты) через префикс ~. Данные в обоих случаях – массив JSON -объектов по одному на каждый элемент списка. В которых перечисляются переменные, отображаемые в контейнере (через @). Также могут быть добавлены любые другие(неотображаемые в интерфейсе). Также в элементе датасета отдельно можно указать key – ключ, возвращаемый при нажатии в специальную переменную (в случае с механизмом датасетов это делать не имеет смысла, так как в датасете у каждой записи есть _id) Пример определения списка карточек в Python (без использования механизма датасетов, с опциями поиска в тулбаре и сохранение позиции) .. code-block:: Python j = { "customcards": { "options":{ "search_enabled":True, "save_position":True }, "layout": "^card1", "cardsdata":[]} } j["customcards"]["cardsdata"].append({"text1":"Какой то заголовок 1","text2":"Подзаголовок №1"}) j["customcards"]["cardsdata"].append({"text1":"Какой то заголовок 2","text2":"Подзаголовок №2"}) hashMap.put("cards",json_to_str(j)) Пример Таблицы с использование датасетов, без опций: .. code-block:: Python j = { "customtable": { "layout": "^item", "tabledata":"~goods"} } При нажатии на элемент списка генерируется событие CardsClick. В переменных появляется: * **selected_card_key** – ключ, если, а записи есть key * **selected_card_position** – позиция списка * **selected_card_data** (если это датасет, либо если не датасет, то если заранее включен флаг "return_selected_data") – целиком запись в виде JSON-элемента массива Переопределение макета любого элемента списка """""""""""""""""""""""""""""""""""""""""""""""""" С релиза 10.35 можно для любого элемента customcards и customtable сделать свой дизайн на основе отдельного контейнера (например выделить цветом), абсолютно любого содержания. Для этого в rows в конкретном элементе надо использовать _layout и передать туда нужный макет. Таким образом, можно сделать каждый элемент со своим дизайном. .. code-block:: Python j = { "customtable": { "layout": "^card1", "tabledata":[]} } j["customtable"]["tabledata"].append({"text1":"Какой то заголовок 1","text2":"Подзаголовок №1"}) j["customtable"]["tabledata"].append({"_layout":"^card2","text1":"Какой то заголовок 2","text2":"Подзаголовок №2"}) j["customtable"]["tabledata"].append({"text1":"Какой то заголовок 3","text2":"Подзаголовок №3"}) hashMap.put("cards",json_to_str(j)) Выборочная раскраска элементов списка """""""""""""""""""""""""""""""""""""""" В списках можно делать произвольный макет каждого элемента (через "_layout"), в том числе и раскраску, но это затратная технология (на больших списках и медленных устройствах сказывается на быстродействии). Поэтому добавлена просто «раскраска» (меняется только цвет фона, а не весь макет) В опциях таблицы должно быть включено **"colorizing"**, и тогда на элементы можно использовать свойство **_background** с указанием цвета в HEX-формате. Использование активных элементов в списках """""""""""""""""""""""""""""""""""""""""""" .. image:: _static/2025_screen_18.png :scale: 90% :align: center В элементах списков доступно использование активных элементов: Кнопки, Меню и Флажки. Для этого их нужно разместить в контейнере в любом количестве и любом месте. При нажатии они будут порождать отдельное от нажатия на саму карточку событие с **listener=LayoutAction**, также помещается переменные **layout_listener** и card_data. В первой содержится переменная элемента, породившего событие (кнопки, пункта меню, флажка). Во второй - данные карточки, включая позицию в виде JSON-строки. Для добавления меню нужно поместить в контейнер элемент **PopupMenuButton**, в качестве значения передать список пунктов меню, разделенных точкой с запятой. Например "Первый;Второй" Флажок ведет себя как обычный флажок, плюс выполняет необходимое действие - при изменении состояния, он прописывает значение состояния в датасет переменной списка, чтобы при обновлении списка показать текущее состояние. Использование поиска, переопределение поиска """"""""""""""""""""""""""""""""""""""""""""""""""" .. note:: Для датасетов поиск устроен по-другому. См. главу "Датасеты". Для того, чтобы на экране появился автоматический поиск по таблице в тулбаре, необходимо добавить в JSON списка в раздел «options» поле **search_enabled** с значением True. Также по желанию, можно передать поле, по котору будет вестись поиск **search_string** - это ключ, в который для каждой карточки можно поместить строку с данными поиска. Поиск будет вестись по вхождению подстроки поиска в эту строку. Если такого поля нет в карточке, то будет вестись поиск по всем полям объекта данных. Этот поиск можно переопределить - пустить вводимый текст на события. Для этого в раздел «options» необходимо добавить **override_search** с значением True, тогда при вводе текста в поле поиска будет генерироваться событие **Search**, а в переменную **SearchString** будет попадать введенный текст Горизонтальные списки """""""""""""""""""""""" .. image:: _static/2025_screen_19.png :scale: 75% :align: center Опция **horizontal**:True делает список горизонтальным. Также, если требуется чтобы карточка была не на всю ширину экрана, а допустим на какую то его часть, нужно указать опцию "width_ratio" – с процентом от ширины экрана, например 50 - будет половина экрана. Если не указать, то карточка будет на всю ширину Сохранение позиции в списке """"""""""""""""""""""""""""" Для того, чтобы на экране появился автоматический поиск по таблице в тулбаре, необходимо добавить в JSON списка в раздел "options" поле «save_position» с значением True. При наступлении события и перерисовке списка, список останется на том же месте. Использование групп """""""""""""""""""""""" Можно сгруппировать список карточек (работает только со списком карточек). Для этого в нужных местах датасета нужно вставить объекты с полем «group» (предопределенное поле) Догрузка данных при промотке """"""""""""""""""""""""""""""" Для больших списков можно сделать догрузку – получение порций данных, когда пользователь промотал до конца. При промотке дальше возникает событие LoadMoreItems, оформленное прогресс баром, в котором разработчик может определить обработчик добавления новой порции строк в переменную AdditionalItemsData. Позиционирование на позицию в списке карточек и таблице """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" Теперь можно мгновенно или плавно переместиться на выбранную позицию двумя простыми командами: * **ListGoTo**, номер позиции – мгновенное перемещение на позицию * **ListGoToSmooth**, номер позиции – анимированное перемещение на позицию Поля датасетов ~~~~~~~~~~~~~~~~~~ .. image:: _static/2025_screen_20.png :scale: 75% :align: center Можно разместить на экране ссылочные поля ввода данных, содержащие ссылки на записи датасетов Вы просто указывает переменную в которой храниться или будет храниться значение поля в виде ссылки и датасет в значении. И всё. Пользователь просто выбирает запись из списка, пользуется поиском если надо. При выборе в переменную также попадает универсальная ссылка. Для такого случая у датасета желательно в опциях определить 2 вещи: * Представление записи – опция view_template. Можно использовать html. Имена полей указываются в фигурных скобках. Можно разместить в представлении несколько полей. Например {name}, {barcode}. Можно использовать html. Например {name}:{article} * Можно указать форму элементов списка list_layout – имя контейнера (по умолчанию AUTO) Пример создания и указания опций датасета: .. code-block:: Python datasrv = CreateDataSet("goods") datasrv.setOptions(json_to_str({"list_layout":"item","view_template":"{name} , {article}"})) Можно использовать конструкцию с | чтобы разместить поле с заголовком Для задания настройки полей есть упрощенный вариант и вариант с настройками. Упрощенный вариант приведен выше, а для настроек необходимо указать JSON-настройки (обычно - через переменную) * dataset (обязательно) – имя датасета * inline – поиск по строке непосредственно в поле * select – кнопка выбора из списка * spinner – выбор из списка (аналог выпадающего списка) заменяет опцию inline * hint - подсказка .. image:: _static/2025_screen_22.png :scale: 75% :align: center Выбранные и предустановленные значения """""""""""""""""""""""""""""""""""""""""" Везде используется универсальная ссылка - как результат пользовательского выбора, так и для установки предопределенных значений. Например, создадим датасет nds .. code-block:: Python datasrv = CreateDataSet("nds") datasrv.setOptions(json_to_str({"view_template":"Ставка - {name}"})) nds_list = [] nds_list .append({"name":"10%","_id":"НДС10"}) nds_list .append({"name":"20%","_id":"НДС20"}) nds_list .append({"name":"0%","_id":"НДС0"}) datasrv.put(json_to_str(nds_list)) И на экране в onStart установим НДС по умолчанию .. code-block:: Python hashMap.put("nds","nds$НДС20") Тогда, при открытии, увидим результат: .. image:: _static/2025_screen_21.png :scale: 55% :align: center ActiveCV ~~~~~~~~~~~ .. image:: _static/2025_screen_23.png :scale: 55% :align: center CV-возможности подробно разобраны в разделе ActiveCV Поле карты ------------- Векторные карты подробно разобраны в разделе Векторный редактор HTML-поле ~~~~~~~~~~~~~ .. image:: _static/2025_screen_24.png :scale: 55% :align: center .. note:: В данном разделе речь идет о элементе экрана HTML, но по большей части это справедливо и для команды PrintPreview которая открывает HTML-поле для предпросмотра и печати. На экране можно разместить HTML-поле. В значение нужно передать HTML-документ, тогда отобразится в виде HTML. Также этот элемент содержит поддержку Javascript и может выполнять cкрипты в документе. Связь с Андроид-приложением (генерация события в SimpleUI) происходит через функцию onInput Пример реализации обработчиков кнопок. .. code-block:: HTML Для подготовки HTML можно использовать, например шаблонизатор Jinja. Подробнее о приемах разработки написано тут https://infostart.ru/1c/articles/1760354/ Кстати, файлы макетов необязательно хранить в виде строк в обработчиках, для этой цели вполне хорошо подходят Медиафайлы. Можно прицепить несколько макетов к конфигурации и использвоать их. Произвольная разметка экранов (XML-разметка) ----------------------------------------------- Альтернатива конструктору и контейнерам - произвольная разметка экранов, в том виде, в котором она существует в нативной разработке. С соответствующими инструментами работы. Подробно описана в статье https://infostart.ru/1c/articles/1983895/