Работа с qainspector
UI Приложений на ОС Аврора построен с помощью QML страниц. Для просмотра структуры страниц был создан инструмент qainspector.
- Предварительные условия
- Установка и запуск
- Панель управления
- Структура страницы
- Работа с UI элементами
Предварительные условия
Для успешной работы с qainpesctor требуется, чтобы на устройстве был установлен и активирован
qtium-driver (через соответствующий пункт меню Режима разработчика в Настройках).
Установка и запуск
В данном руководстве рассматривается работа qainspector на примере ОС Ubuntu 24.04.1.
При работе с другими операционными системами процесс установки и запуска необходимо адаптировать в соответствии с их особенностями.
-
Подготовка:
sudo add-apt-repository ppa:alexlarsson/flatpak sudo apt install flatpak flatpak-builder flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo git clone https://hub.mos.ru/auroraos/testing/qainspector cd qainspectorКоманды в данной инструкции приводятся для Unix-совместимого терминала. В Linux и macOS можно использовать стандартный терминал, в Windows необходимо использовать Git Bash.
-
Сборка:
flatpak-builder --user --install-deps-from=flathub --force-clean --repo=repo build ru.omprussia.qainspector.json flatpak build-bundle --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo $(pwd)/repo ru.omprussia.qainspector.flatpak ru.omprussia.qainspector stable -
Установка:
flatpak --user install ru.omprussia.qainspector.flatpak -
Запуск:
flatpak run ru.omprussia.qainspector
Панель управления
Для начала работы с утилитой в левом верхнем углу необходимо задать IP адрес устройства (например, 192.168.2.15),
номер порта оставить по умолчанию 8888 и нажать Connect:

После успешного установления соединения с приложением на панели появятся кнопки для "дампа" (вывода) деревьев элементов:

Dump tree— выводит полное дерево элементов, начиная с главной страницы и далее по вложенным страницам;Dump page— выводит дерево элементов конкретной страницы;Dump cover— выводит дерево элементов обложки приложения.
Структура страницы
Ниже приведены различные представления структуры одной и той же страницы.
-
Представление страницы в QML коде:
import QtQuick 2.5 import Sailfish.Silica 1.0 import org.nemomobile.calendar 1.0 Page { id: root property QtObject event property QtObject occurrence property var saveStartedCb property bool _smallLandscape: isLandscape && Screen.sizeCategory <= Screen.Medium Label { objectName: "recurringEvent" x: Theme.horizontalPageMargin y: _smallLandscape ? Theme.paddingLarge : Theme.itemSizeExtraLarge width: parent.width - Theme.horizontalPageMargin * 2 color: Theme.highlightColor font.pixelSize: Theme.fontSizeExtraLarge wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter //% "This is a recurring event" text: qsTrId("calendar-event-he-edit_recurring") } Column { anchors { bottom: parent.bottom bottomMargin: _smallLandscape ? Theme.itemSizeExtraSmall : Theme.itemSizeMedium horizontalCenter: parent.horizontalCenter } spacing: Theme.paddingMedium Button { id: editThisEvent objectName: "editThisEvent" width: editSeries.width //% "Change event" text: qsTrId("calendar-event-change_occurrence") onClicked: { if (isTablet) { root.pageContainer.animatorReplace(eventEditPageComponent, { event: root.event, occurrence: root.occurrence, saveStartedCb: root.saveStartedCb }) } else { root.pageContainer.animatorReplace("EventEditPage.qml", { event: root.event, occurrence: root.occurrence, saveStartedCb: root.saveStartedCb }) } } } Label { objectName: "editThisEventDecription" width: editThisEvent.width color: Theme.highlightColor font.pixelSize: Theme.fontSizeSmall wrapMode: Text.Wrap //% "The changes will not affect other events in the series." text: qsTrId("calendar-event-change_occurrence_description") } Item { width: parent.width height: Theme.paddingLarge * 2 } Button { id: editSeries objectName: "editSeries" preferredWidth: Theme.buttonWidthLarge //% "Change the series" text: qsTrId("calendar-event-change_all_occurrences") onClicked: { if (isTablet) { root.pageContainer.animatorReplace(eventEditPageComponent, { event: root.event }) } else { root.pageContainer.animatorReplace("EventEditPage.qml", { event: root.event }) } } } Label { objectName: "editSeriesDecription" width: editSeries.width color: Theme.highlightColor font.pixelSize: Theme.fontSizeSmall wrapMode: Text.Wrap //% "Changes will be made to all events in the series." text: qsTrId("calendar-event-change_all_occurrences_description") } } } -
UI представление страницы:

-
Представление страницы в qainpector:

По нажатию правой кнопки мыши на конкретном UI элементе откроется окно с его свойствами:

Работа с UI элементами
Для работы с определенным UI элементом необходимо его выделить из списка других в дереве страницы, для этого используются селекторы.
qainspector позволяет осуществлять поиск элементов по следующим атрибутам:
СlassName,Text,ObjectName,XPath.
На нижней панели qainpector располагается поле «Search pattern»:

Поле заполняется соответствующими атрибутами из списка выше. При нажатии на кнопку поиска «Search» можно убедиться, что выделяется нужный элемент. Затем, используя этот селектор в коде, можно быть уверенным, что qtium-driver будет взаимодействовать именно с этим элементом.
Поиск по ClassName
У всех элементов на странице есть класс.
При открытии свойства элемента можно увидеть значение класса в поле ClassName.
На странице из примера выше есть две кнопки с атрибутом ClassName: Button.
При поиске по ClassName qtium-driver обратится к первому элементу, имеющему такой же ClassName.
Поиск по Text
Часть элементов, таких как LabelBase, имеют атрибут Text, в котором записано текстовое значение.
Например, на странице выше есть элемент LabelBase, у которого в поле Text записано значение
«Это повторяющееся событие».
Можно найти данный элемент LabelBase, используя это значение в поиске и установив радиокнопку Text.
Поиск по objectName
Это самый однозначный, оптимизированный и быстрый способ найти нужный элемент.
Однако не стоит переусердствовать с добавлением objectName при разработке собственного приложения,
так как чрезмерно большое количество таких атрибутов может привести к замедлению работы UI.
В коде автоматических тестов для поиска по objectName нужно указывать критерий поиска By.ID.
Поиск по XPath
В работе с приложениями далеко не всегда удается найти простой и уникальный селектор для элемента.
Лучшим вариантом решения такой проблемы является добавление objectName, однако это не всегда
возможно сделать быстро и удобно.
В такой случаях рекомендуется использовать язык запросов XPath.
XPath (XML Path Language) — это язык запросов, который использует древовидную структуру документа. С синтаксисом языка можно ознакомиться здесь.
Существуют следующие базовые правила для работы с XPath:
-
XPath запрос всегда начинается с символа
/или//. Они используются следующим образом:el1/el2— выбирает элементы el2, являющиеся прямыми потомками el1;el1//el2— выбирает элементы el2, являющиеся потомками el1 любой степени вложенности.
-
'[ ]'— команда фильтрации. Если по запросу найдено несколько элементов, то будет произведена фильтрация по правилу, указанному в скобках. Существует множество вариантов фильтрации, ниже приведены некоторые из них:- по атрибуту — например, если нужно найти элемент с классом
LabelBase, у которого атрибут text содержит определенное значение, то поиск осуществляется так://LabelBase[text()='Изменить событие']; - по индексу — при наличии нескольких элементов можно обратиться к определённому элементу в списке, например,
//Button[2]найдет второй элемент на странице с классомButton; - по частичному совпадению атрибута — например,
//LabelBase[contains(text(), 'Сегодня')]найдет элементы, у которых классLabelBaseи текст содержит слово "Сегодня", например "Сегодня, 20 апреля".
- по атрибуту — например, если нужно найти элемент с классом
-
Поиск по классу в XPath зависит от регистра.