Spec-Zone.ru › Qt 5.6

Доступность для приложений QWidget

Введение

Мы сосредоточимся на интерфейсе Qt для доступности QAccessibleInterface и на том, как сделать приложения доступными.

Доступность в приложениях на основе QWidget

При взаимодействии с технологиями вспомогательной доступности нам необходимо описать пользовательский интерфейс Qt таким образом, чтобы они могли его понять. Приложения Qt используют QAccessibleInterface для отображения информации об отдельных элементах пользовательского интерфейса. В настоящее время Qt поддерживает свои виджеты и части виджетов, например, ползунки, но при необходимости интерфейс можно реализовать для любого QObject. QAccessible содержит перечисления, описывающие пользовательский интерфейс. Описание в основном основано на MSAA и независимо от Qt. В ходе данного документа мы рассмотрим перечисления.

Структура пользовательского интерфейса представлена в виде дерева подклассов QAccessibleInterface. Вы можете представить это как представление пользовательского интерфейса, подобное дереву QObject, построенному Qt. Объекты могут быть виджетами или частями виджетов (например, ручки полосы прокрутки). Мы подробно рассмотрим дерево в следующем разделе.

Серверы уведомляют клиентов о изменениях объектов с помощью updateAccessibility(), отправляя события, а клиенты регистрируются для получения событий. Доступные события определяются перечислением QAccessible::Event. Затем клиенты могут запросить объект, который сгенерировал событие, с помощью QAccessible::queryAccessibleInterface().

Члены и перечисления в QAccessible используются для описания доступных объектов:

  • Роль: Описывает роль, которую объект выполняет в пользовательском интерфейсе, например, является ли он окном, полем редактирования текста или ячейкой в таблице.
  • Связь: Описывает взаимоотношения между объектами в иерархии объектов.
  • Состояние: Объекты могут находиться в различных состояниях. Примеры состояний: является ли объект отключенным, имеет ли он фокус или предоставляет ли он всплывающее меню.

Клиенты также могут получить содержимое объектов, например, текст кнопки; объект предоставляет строки, определенные перечислением QAccessible::Text, которые предоставляют информацию о содержимом.

Дерево доступных объектов

Как упоминалось ранее, структура дерева создается из доступных объектов приложения. Перемещаясь по дереву, клиенты могут получить доступ ко всем элементам пользовательского интерфейса. Взаимоотношения объектов предоставляют клиентам информацию о пользовательском интерфейсе. Например, ручка ползунка является потомком ползунка, к которому она принадлежит. QAccessible::Relation описывает различные отношения, которые клиенты могут запросить у объектов.

Обратите внимание, что нет прямой сопоставления между деревом Qt QObject и деревом доступных объектов. Например, ручки полосы прокрутки являются доступными объектами, но не являются виджетами или объектами в Qt.

Клиенты AT имеют доступ к дереву доступных объектов через корневой объект в дереве, который является QApplication. Они могут перемещаться по дереву с помощью функций QAccessibleInterface::parent(), QAccessibleInterface::childCount() и QAccessibleInterface::child().

Qt предоставляет интерфейсы доступности для своих виджетов и для Qt Quick Controls. Интерфейсы для любого подкласса QObject могут быть запрошены через QAccessible::queryInterface(). Если более специализированный интерфейс не определен, предоставляется реализация по умолчанию. Клиент AT не может получить интерфейс для доступных объектов, не имеющих эквивалента QObject, например, ручек полосы прокрутки, но они отображаются как обычные объекты через интерфейсы родительских доступных объектов, например, вы можете запросить их взаимоотношения с помощью QAccessibleInterface::relations().

Для иллюстрации мы представляем изображение дерева доступных объектов. Под деревом находится таблица с примерами взаимоотношений объектов.

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

Исходный объект Целевой объект Связь
Ползунок Индикатор Управляющий
Индикатор Ползунок Управляемый
Ползунок Приложение Предок
Приложение Ползунок Потомок
Кнопка Индикатор Брат

Статические функции QAccessible

Доступность управляется статическими функциями QAccessible, которые мы вскоре рассмотрим. Они создают интерфейсы QAccessible, строят дерево объектов и инициируют подключение к MSAA или другим платформам технологий. Если вас интересует только то, как сделать ваше приложение доступным, вы можете пропустить этот раздел и перейти к разделу Реализация доступности.

Взаимодействие между клиентами и сервером инициируется при вызове setRootObject(). Это делается при создании экземпляра QApplication, и вам не нужно делать это самостоятельно.

Когда объект QObject вызывает updateAccessibility(), клиенты, которые следят за событиями, уведомляются о изменении. Функция используется для отправки событий технологиям вспомогательной доступности, и события доступности событий отправляются методом updateAccessibility().

queryAccessibleInterface() возвращает доступные интерфейсы для QObjects. Все виджеты в Qt предоставляют интерфейсы; если вам нужны интерфейсы для управления поведением других подклассов QObject, вы должны реализовать интерфейсы самостоятельно, хотя удобный класс QAccessibleObject реализует часть функциональности за вас.

Фабрика, которая создает интерфейсы доступности для QObjects, является функцией типа QAccessible::InterfaceFactory. Можно установить несколько фабрик. Последняя установленная фабрика будет первой, к которой обратятся за интерфейсами. queryAccessibleInterface() использует фабрики для создания интерфейсов для QObjects. Обычно вам не нужно беспокоиться о фабриках, потому что вы можете реализовать плагины, которые создают интерфейсы. Позднее мы приведем примеры обоих подходов.

Реализация доступности

Для обеспечения поддержки доступности для виджета или другого элемента пользовательского интерфейса необходимо реализовать QAccessibleInterface и распространить его в QAccessiblePlugin. Также можно скомпилировать интерфейс в приложение и предоставить QAccessible::InterfaceFactory для него. Фабрика может быть использована, если вы линковались статически или не хотите добавлять сложность плагинов. Это может быть преимуществом, если, например, вы предоставляете стороннюю библиотеку.

Все виджеты и другие элементы пользовательского интерфейса должны иметь интерфейсы и плагины. Если вы хотите, чтобы ваше приложение поддерживало доступность, вам следует учесть следующее:

  • Qt уже реализовал доступность для собственных виджетов. Поэтому мы рекомендуем использовать виджеты Qt по возможности.
  • Для каждого элемента, который вы хотите предоставить клиентам доступности, должен быть реализован QAccessibleInterface.
  • Необходимо отправлять события доступности из реализованных вами пользовательских элементов пользовательского интерфейса.

В общем случае рекомендуется иметь некоторое знакомство с MSAA, для которого изначально была разработана поддержка доступности Qt. Вы также должны изучить значения перечислений QAccessible, которые описывают роли, действия, отношения и события, которые вам необходимо учитывать.

Обратите внимание, что вы можете изучить, как виджеты Qt реализуют свою доступность. Одна из основных проблем со стандартом MSAA заключается в том, что интерфейсы часто реализуются несогласованным образом. Это затрудняет работу клиентов и часто приводит к предположениям о функциональности объектов.

Можно реализовать интерфейсы, унаследовав QAccessibleInterface и реализовав его чистые виртуальные функции. Однако на практике обычно предпочтительнее наследоваться от QAccessibleObject или QAccessibleWidget, которые реализуют часть функциональности за вас. В следующем разделе мы рассмотрим пример реализации доступности для виджета, унаследовав класс QAccessibleWidget.

Удобные классы QAccessibleObject и QAccessibleWidget

При реализации интерфейса доступности для виджетов, как правило, наследуются от QAccessibleWidget, который является удобным классом для виджетов. Еще один доступный удобный класс, который наследуется от QAccessibleWidget, это QAccessibleObject, который реализует часть интерфейса для QObjects.

Класс QAccessibleWidget предоставляет следующие возможности:

  • Обрабатывает навигацию по дереву и проверку попадания по объектам.
  • Обрабатывает события, роли и действия, общие для всех QWidget.
  • Обрабатывает действия и методы, которые можно выполнить для всех виджетов.
  • Вычисляет прямоугольники ограничивающих областей с помощью rect().
  • Предоставляет строки text(), подходящие для обычного виджета.
  • Устанавливает состояния, общие для всех виджетов.

Пример QAccessibleWidget

Вместо создания пользовательского виджета и реализации для него интерфейса, мы покажем, как реализуется доступность для одного из стандартных виджетов Qt: QSlider. Доступный интерфейс, QAccessibleSlider, наследуется от QAccessibleAbstractSlider, который, в свою очередь, наследуется от QAccessibleWidget. Вам не нужно изучать класс QAccessibleAbstractSlider, чтобы прочитать этот раздел. Если вы хотите взглянуть, код всех доступных интерфейсов Qt находится в src/plugins/accessible/widgets. Вот конструктор QAccessibleSlider:

QAccessibleSlider::QAccessibleSlider(QWidget *w)
: QAccessibleAbstractSlider(w)
{
    Q_ASSERT(slider());
    addControllingSignal(QLatin1String("valueChanged(int)"));
}

Ползунок — это сложное управление, которое функционирует как Контроллер для его доступных дочерних элементов. Эта взаимосвязь должна быть известна интерфейсу (для parent(), child() и relations()). Это можно сделать с помощью управляющего сигнала, который является механизмом, предоставляемым QAccessibleWidget. Мы делаем это в конструкторе:

Выбор показанного сигнала не важен; те же принципы применяются ко всем сигналам, объявленным таким образом. Обратите внимание, что мы используем QLatin1String, чтобы гарантировать, что имя сигнала указано правильно.

Когда доступный объект изменяется таким образом, что пользователи должны об этом знать, он уведомляет клиентов об изменении, отправляя им событие через доступный интерфейс. Вот как QSlider вызывает updateAccessibility(), чтобы указать, что его значение изменилось:

void QAbstractSlider::setValue(int value)
    ...
    QAccessibleValueChangeEvent event(this, d->value);
    QAccessible::updateAccessibility(&event);
    ...
}

Обратите внимание, что вызов выполняется после изменения значения ползунка, потому что клиенты могут запросить новое значение сразу после получения события.

Интерфейс должен уметь вычислять ограничивающие прямоугольники самого себя и любых дочерних элементов, которые не предоставляют собственного интерфейса. У QAccessibleSlider есть три таких дочерних элемента, идентифицируемые с помощью закрытого перечисления SliderElements, которое имеет следующие значения: PageRight (прямоугольник справа от ручки ползунка), и Position (ручка ползунка). Вот реализация rect():

QRect QAccessibleSlider::rect(int child) const
{
    ...
    switch (child) {
    case PageLeft:
        if (slider()->orientation() == Qt::Vertical)
            rect = QRect(0, 0, slider()->width(), srect.y());
        else
            rect = QRect(0, 0, srect.x(), slider()->height());
        break;
    case Position:
        rect = srect;
        break;
    case PageRight:
        if (slider()->orientation() == Qt::Vertical)
            rect = QRect(0, srect.y() + srect.height(), slider()->width(), slider()->height()- srect.y() - srect.height());
        else
            rect = QRect(srect.x() + srect.width(), 0, slider()->width() - srect.x() - srect.width(), slider()->height());
        break;
    default:
        return QAccessibleAbstractSlider::rect(child);
    }
    ...

Первая часть функции, которую мы опустили, использует текущий стиль, чтобы вычислить ограничивающий прямоугольник ручки ползунка; он хранится в srect. Обратите внимание, что дочерний элемент 0, охваченный в случае по умолчанию в приведенном выше коде, — это сам ползунок, поэтому мы можем просто вернуть ограничительный прямоугольник QSlider, полученный от суперкласса, что фактически является значением, полученным от QAccessibleWidget::rect().

    QPoint tp = slider()->mapToGlobal(QPoint(0,0));
    return QRect(tp.x() + rect.x(), tp.y() + rect.y(), rect.width(), rect.height());
}

Перед возвращением прямоугольника он должен быть сопоставлен с координатами экрана.

QAccessibleSlider должен повторно реализовать QAccessibleInterface::childCount(), так как он управляет дочерними элементами без интерфейсов.

Функция text() возвращает строки QAccessible::Text для ползунка:

QString QAccessibleSlider::text(Text t, int child) const
{
    if (!slider()->isVisible())
        return QString();
    switch (t) {
    case Value:
        if (!child || child == 2)
            return QString::number(slider()->value());
        return QString();
    case Name:
        switch (child) {
        case PageLeft:
            return slider()->orientation() == Qt::Horizontal ?
                QSlider::tr("Page left") : QSlider::tr("Page up");
        case Position:
            return QSlider::tr("Position");
        case PageRight:
            return slider()->orientation() == Qt::Horizontal ?
                QSlider::tr("Page right") : QSlider::tr("Page down");
        }
        break;
    default:
        break;
    }
    return QAccessibleAbstractSlider::text(t, child);
}

Функция slider() возвращает указатель на QSlider интерфейса. Некоторые значения оставлены для реализации суперкласса. Не все значения подходят для всех доступных объектов, как вы можете видеть в случае QAccessible::Value. В этих случаях, где нельзя предоставить соответствующий текст, вы должны просто вернуть пустую строку.

Реализация функции role() проста:

QAccessible::Role QAccessibleSlider::role(int child) const
{
    switch (child) {
    case PageLeft:
    case PageRight:
        return PushButton;
    case Position:
        return Indicator;
    default:
        return Slider;
    }
}

Функция role должна быть переопределена всеми объектами и описывает роль себя и дочерних элементов, которые не предоставляют собственных доступных интерфейсов.

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

QAccessible::State QAccessibleSlider::state(int child) const
{
    const State parentState = QAccessibleAbstractSlider::state(0);
    ...
    switch (child) {
    case PageLeft:
        if (slider->value() <= slider->minimum())
            state |= Unavailable;
        break;
    case PageRight:
        if (slider->value() >= slider->maximum())
            state |= Unavailable;
        break;
    case Position:
    default:
        break;
    }

    return state;
}

Реализация суперкласса state() использует реализацию QAccessibleInterface::state(). Нам просто нужно отключить кнопки, если ползунок находится в своем минимальном или максимальном положении.

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

Обработка запросов действий от клиентов

Приложения могут раскрывать действия, которые могут быть вызваны клиентом. Для поддержки действий в объекте необходимо унаследовать от QAccessibleActionInterface.

Интерактивные элементы должны раскрывать функциональность, вызываемую взаимодействием с мышью, например. Кнопка должна, например, реализовывать действие нажатия.

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

Вам необходимо повторно реализовать actionNames(), чтобы вернуть список всех действий, которые поддерживает объект. Этот список не должен быть локализован.

Существуют две функции, предоставляющие информацию о действиях, которые должны возвращать локализованные строки: localizedActionName() и localizedActionDescription(). Эти функции могут использоваться клиентом для представления действий пользователю. В общем случае имя должно быть кратким и состоять только из одного слова, например, «нажать».

Доступен список стандартных имен действий и локализаций, которые следует использовать, когда действие подходит. Это упрощает клиентам понимание семантики, и Qt будет пытаться правильно отобразить их на разных платформах.

Конечно, действие также должно иметь способ запуска. doAction() должно вызывать действие, как рекламируется по имени и описанию.

Чтобы увидеть примеры реализации действий и методов, вы можете изучить реализации стандартных виджетов Qt, таких как QAccessiblePushButton.

Реализация доступных плагинов

В этом разделе мы объясним процедуру реализации доступных плагинов для ваших интерфейсов. Плагин — это класс, хранящийся в библиотеке общего использования, который может быть загружен во время выполнения. Распространение интерфейсов в виде плагинов удобно, так как они будут загружаться только по мере необходимости.

Создание доступного плагина достигается путем наследования от QAccessiblePlugin, определения поддерживаемых имен классов в описании плагина в формате JSON и повторной реализации create() из QAccessiblePlugin. Файл .pro необходимо изменить, используя шаблон плагина, а библиотека, содержащая плагин, должна быть размещена в пути, по которому Qt ищет доступные плагины.

Мы пройдемся по реализации SliderPlugin, которая является доступным плагином, генерирующим интерфейс QAccessibleSlider из Примера QAccessibleWidget. Мы начнем с функции key():

QStringList SliderPlugin::keys() const
{
    return QStringList() << QLatin1String("QSlider");
}

Нам просто нужно вернуть имя класса единственного интерфейса, для которого наш плагин может создать доступный интерфейс. Плагин может поддерживать любое количество классов; просто добавьте больше имен классов в список строк. Переходим к функции create():

QAccessibleInterface *SliderPlugin::create(const QString &classname, QObject *object)
{
    QAccessibleInterface *interface = 0;

    if (classname == QLatin1String("QSlider") && object && object->isWidgetType())
        interface = new QAccessibleSlider(static_cast<QWidget *>(object));

    return interface;
}

Мы проверяем, является ли запрашиваемый интерфейс для QSlider; если да, мы создаем и возвращаем интерфейс для него. Обратите внимание, что object всегда будет экземпляром classname. Если вы не поддерживаете класс, вы должны вернуть 0. updateAccessibility() проверяет доступные плагины с поддержкой доступности, пока не найдет тот, который не вернет 0.

Наконец, вам нужно включить макросы в файле cpp:

    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.Accessibility.SliderPlugin" FILE "slider.json")

Макрос Q_PLUGIN_METADATA экспортирует плагин в SliderPlugin класс в acc_sliderplugin библиотеку. Первый аргумент — IID плагина, а второй — необязательный файл JSON, содержащий метаданные о плагине. Для получения дополнительной информации о плагинах вы можете обратиться к обзору документов по плагинам.

Не имеет значения, требуется ли вам статическая или динамическая компоновка плагина с приложением.

Реализация фабрик интерфейсов

Если вы не хотите предоставлять плагины для своих доступных интерфейсов, вы можете использовать фабрику интерфейса (QAccessible::InterfaceFactory), что является рекомендуемым способом предоставления доступных интерфейсов в статически связанном приложении.

Фабрика — это указатель на функцию, которая принимает те же параметры, что и QAccessiblePlugin's create() — QString и QObject. Она также работает аналогично. Вы устанавливаете фабрику с помощью функции installFactory(). Мы даем пример того, как создать фабрику для интерфейса QAccessibleSlider:

QAccessibleInterface *sliderFactory(const QString &classname, QObject *object)
{
    QAccessibleInterface *interface = 0;

    if (classname == QLatin1String("QSlider") && object && object->isWidgetType())
        interface = new QAccessibleSlider(static_cast<QWidget *>(object));

    return interface;
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QAccessible::installFactory(sliderFactory);
    ...
}

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.6/accessible-qwidget.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API