Spec-Zone.ru › Qt 5.11

Доступность для приложений 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() возвращает интерфейсы доступности для QObject. Все виджеты в Qt предоставляют интерфейсы; если вам нужны интерфейсы для управления поведением других подклассов QObject, вы должны реализовать эти интерфейсы самостоятельно, хотя удобный класс QAccessibleObject реализует часть функциональности за вас.

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

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

Для обеспечения поддержки доступности для виджета или другого элемента пользовательского интерфейса необходимо реализовать 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, которое имеет следующие значения: PageLeft (прямоугольник слева от ручки ползунка), 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.11/accessible-qwidget.html

Spec-Zone.ru

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