Использование pyatspi2 для исследования графических пользовательских интерфейсов

Дата публикации:06.04.2023
Поделиться в Twitter Поделиться в F******k Поделиться в VKontakte Поделиться в Telegram Поделиться в Mastodon

Основой для статьи послужил материал PyAtSpi2Example.

Введение

Спецификация AT-SPI представляет собой набор протоколов и соглашений о взаимодействии операционной системы, прикладных программ и вспомогательных технологий с целью обеспечить доступ к графическому пользовательскому интерфейсу для лиц с различными ограничениями здоровья, нарушающими работу органов чувств, когнитивные функции и / или координацию движений. AT-SPI применяется в графических пользовательских окружениях дистрибутивов GNU/Linux и стандартизована в группе стандартов ISO/IEC 13066.

Для того чтобы изучить, как элементы пользовательского интерфейса представлены средствами AT-SPI для вспомогательных технологий, подойдёт модуль pyatspi, содержащий привязки AT-SPI для python. Также pyatspi будет полезен для создания автоматизированных тестов доступности пользовательского интерфейса приложения и для проверки работоспособности пользовательского интерфейса приложения в целом. Стоит отметить, что во операционных системах инфраструктура поддержки вспомогательных технологий часто совмещена со средствами автоматического тестирования пользовательского интерфейса, поскольку обе задачи базируются на машиночитаемой информации, которую о своём интерфейсе предоставляет приложение и которую затем пожно использовать либо для проверки работоспособности виджетов (например, инструмент Accerciser), либа для представления его пользователю невизуальным способом (например, программа экранного доступа Orca, использующая синтез речи и тактильный вывод).

В пакетных базах многих дистрибутивов GNU/Linux уже имеются готовые сборки pyatspi. Например, в Ubuntu pyatspi можно установить командой с правами root:

apt install python-pyatspi

Исследование дерева AT-SPI

В системах графического пользовательского интерфейса элементы, как правило, организованы в виде иерархического дерева со связями родитель (контейнер) — потомки, которые сами могут быть контейнерными элементами для потомков следующего уровня, а могут и не содержать дочерних элементов. По этой причине представление пользовательского интерфейса для целей вспомогательных технологий (assistive technology) выполняется в виде иерархического дерева, которое часто называют деревом доступности (accessibility three). Спецификация AT-SPI не является исключением и экспонирует элементы управления (виджеты) пользовательского интерфейса в виде иерархического дерева AT-SPI, навигацию по которому можно осуществлять, используя API модуля pyatspi.

Конечно, сначала нужно импортировать пространство имён модуля pyatspi:

import pyatspi

Затем можно перечислить активные приложения:

desktop = pyatspi.Registry.getDesktop(0)
for application in desktop:
print(application.name)

Теперь посмотрим, чтчто представляет собой приложение в смысле пользовательского интерфейса:

a = desktop[0]
print(a.name)
for o in a:
print(o)

Пример вывода (зависит от того, какие приложения открыты во время выполнения кода):

gucharmap
[frame | Character Map]

Следовательно, это приложение gucharmap, которое имеет одно окно (фрейм), с заголовком «Character Map». Этот объект, как и остальные элементы дерева AT-SPI, позволяет взаимодействовать с объектами доступности (accessibility object).

Итак, что можно получить:

for o in a:
print(o.role, o.name)

Вывод:

 Character Map

Это значения свойств объекта доступности, связанного с окном приложения (фреймом) — роль и наименование.

Теперь Перейдём на один уровень глубже:

frame = a[0]
for o in frame:
print(o)

Вывод:

[panel | ]
[menu bar | ]

Следовательно, фрейм содержит панель и строку меню.

Теперь покажем содержимое панели:

panel = frame[0]
for o in panel:
print(o)

Вывод:

[status bar | U+10000 LINEAR B SYLLABLE B008 A]
[text | test]
[push button | Copy]
[label | Text to copy:]
[split pane | ]
[filler | Font]

Панель содержит, помимо прочего, поле для ввода текста и метку. У них разные роли, но эти объекты реализуют интерфейс для получения текстового содержимого, что позволяет манипулировать этим содержимым:

t = panel[1]
l = panel[3]
print(t.role)
print(l.role)
t_text = t.queryText()
l_text = l.queryText()

Подробнее о взаимодействии с текстовым содержимым объектов доступности см.: pydoc pyatspi.Text

print(t_text.caretOffset)
print(l_text.caretOffset)

Обратите внимание, что .name виджета может не отличаться, а может отличаться от его содержимого: например, поля для ввода текста обычно имеют метку, которая сообщает, для чего предназначена текстовое поле, так что .name будет содержать текст метки, а текстовое поле будет содержать введённый текст.

От дочернего узла можно вернуться к родительскому:

p = t.parent
print(p == panel)

Для того чтобы отслеживать изменения, происходящие с элементами дерева AT-SPI, рассмотрим, как реагировать на события...

Реакция на события

События AT-SPI — это способ уведомить вспомогательные технологии об изменениях, которые произошли в пользовательском интерфейсе приложения. Такие изменения могут быть вызваны действиями пользователя (активация элементов, перемещение фокуса, ввод текста и т.д.) или самим приложением (всплывающие уведомления, сообщения в строке статуса и т.д.).

Например, чтобы следить за перемещением текстового курсора (каретки), необходимо зарегистрировать обработчик соответствующего события:

def f(e):
print(e.source, " got caret ", e.detail1)
pyatspi.Registry.registerEventListener(f, "object:text-caret-moved")
pyatspi.Registry.start()

Вывод после некоторых действий пользователя:

[text | ] got caret 2
[text | ] got caret 1
[text | ] got caret 0
[entry | ] got caret 23
[entry | ] got caret 24
[entry | ] got caret 25

Этот вывод показывает, что пользователь переместил курсор внутри текстового виджета, а затем в виджет записи.

Схожим образом можно отслеживать перемещение фокуса:

def f(e):
print(e.source, " got focus " if e.detail1 else " lost focus")
pyatspi.Registry.registerEventListener(f, "object:state-changed:focused")
pyatspi.Registry.start()

и это позволит получить интересный вывод для последующей обработки.

Можно отслеживать все варианты изменения состояния, зарегистрировав обработчики для object:state-changed и т.д. Список событий можно узнать из XML-спецификации базового протокола dbus: https://github.com/GNOME/at-spi2-core/blob/master/xml/Event.xml. Например, событие xml Object StateChanged dbus соответствует строке "object:state-changed" при регистрации обработчика в pyatspi2.

Ссылки



Распространение материалов сайта означает, что распространитель принял условия лицензионного соглашения.
Идея и реализация: © Владимир Довыденков и Анатолий Камынин,  2004-2025