Accessibility
|
Source Object | Target Object | Relation |
---|---|---|
Slider | Indicator | Controller |
Indicator | Slider | Controlled |
Slider | Application | Ancestor |
Application | Slider | Child |
PushButton | Indicator | Sibling |
The accessibility is managed by QAccessible's static functions, which we will examine shortly. They produce QAccessible interfaces, build the object tree, and initiate the connection with MSAA or the other platform specific technologies. If you are only interested in learning how to make your application accessible, you can safely skip over this section to Implementing Accessibility.
The communication between clients and the server is initiated when setRootObject() is called. This is done when the QApplication instance is instantiated and you should not have to do this yourself.
When a QObject calls updateAccessibility(), clients that are listening to events are notified of the change. The function is used to post events to the assistive technology, and accessible events are posted by updateAccessibility().
queryAccessibleInterface() returns accessible interfaces for QObjects. All widgets in Qt provide interfaces; if you need interfaces to control the behavior of other QObject subclasses, you must implement the interfaces yourself, although the QAccessibleObject convenience class implements parts of the functionality for you.
The factory that produces accessibility interfaces for QObjects is a function of type QAccessible::InterfaceFactory. It is possible to have several factories installed. The last factory installed will be the first to be asked for interfaces. queryAccessibleInterface() uses the factories to create interfaces for QObjects. Normally, you need not be concerned about factories because you can implement plugins that produce interfaces. We will give examples of both approaches later.
To provide accessibility support for a widget or other user interface element, you need to implement the QAccessibleInterface and distribute it in a QAccessiblePlugin. It is also possible to compile the interface into the application and provide a QAccessible::InterfaceFactory for it. The factory can be used if you link statically or do not want the added complexity of plugins. This can be an advantage if you, for instance, are delivering a 3-rd party library.
All widgets and other user interface elements should have interfaces and plugins. If you want your application to support accessibility, you will need to consider the following:
In general, it is recommended that you are somewhat familiar with MSAA, which Qt originally was built for. You should also study the enum values of QAccessible, which describe the roles, actions, relationships, and events that you need to consider.
Note that you can examine how Qt's widgets implement their accessibility. One major problem with the MSAA standard is that interfaces are often implemented in an inconsistent way. This makes life difficult for clients and often leads to guesswork on object functionality.
It is possible to implement interfaces by inheriting QAccessibleInterface and implementing its pure virtual functions. In practice, however, it is usually preferable to inherit QAccessibleObject or QAccessibleWidget, which implement part of the functionality for you. In the next section, we will see an example of implementing accessibility for a widget by inheriting the QAccessibleWidget class.
When implementing an accessibility interface for widgets, one would as a rule inherit QAccessibleWidget, which is a convenience class for widgets. Another available convenience class, which is inherited by QAccessibleWidget, is the QAccessibleObject, which implements part of the interface for QObjects.
The QAccessibleWidget provides the following functionality:
Instead of creating a custom widget and implementing an interface for it, we will show how accessibility can be implemented for one of Qt's standard widgets: QSlider. Making this widget accessible demonstrates many of the issues that need to be faced when making a custom widget accessible.
The slider is a complex control that functions as a Controller for its accessible children. This relationship must be known by the interface (for relationTo() and navigate()). This can be done using a controlling signal, which is a mechanism provided by QAccessibleWidget. We do this in the constructor:
QAccessibleSlider::QAccessibleSlider(QWidget *w) : QAccessibleAbstractSlider(w) { Q_ASSERT(slider()); addControllingSignal(QLatin1String("valueChanged(int)")); }
The choice of signal shown is not important; the same principles apply to all signals that are declared in this way. Note that we use QLatin1String to ensure that the signal name is correctly specified.
When an accessible object is changed in a way that users need to know about, it notifies clients of the change by sending them an event via the accessible interface. This is how QSlider calls updateAccessibility() to indicate that its value has changed:
void QAbstractSlider::setValue(int value) ... QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged); ... }
Note that the call is made after the value of the slider has changed because clients may query the new value immediately after receiving the event.
The interface must be able to calculate bounding rectangles of itself and any children that do not provide an interface of their own. The QAccessibleSlider has three such children identified by the private enum, SliderElements, which has the following values: PageLeft (the rectangle on the left hand side of the slider handle), PageRight (the rectangle on the right hand side of the handle), and Position (the slider handle). Here is the implementation of 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); } ...
The first part of the function, which we have omitted, uses the current style to calculate the slider handle's bounding rectangle; it is stored in srect. Notice that child 0, covered in the default case in the above code, is the slider itself, so we can simply return the QSlider bounding rectangle obtained from the superclass, which is effectively the value obtained from QAccessibleWidget::rect().
QPoint tp = slider()->mapToGlobal(QPoint(0,0)); return QRect(tp.x() + rect.x(), tp.y() + rect.y(), rect.width(), rect.height()); }
Before the rectangle is returned it must be mapped to screen coordinates.
The QAccessibleSlider must reimplement QAccessibleInterface::childCount() since it manages children without interfaces.
The text() function returns the QAccessible::Text strings for the slider:
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); }
The slider() function returns a pointer to the interface's QSlider. Some values are left for the superclass's implementation. Not all values are appropriate for all accessible objects, as you can see for QAccessible::Value case. You should just return an empty string for those values where no relevant text can be provided.
The implementation of the role() function is straightforward:
QAccessible::Role QAccessibleSlider::role(int child) const { switch (child) { case PageLeft: case PageRight: return PushButton; case Position: return Indicator; default: return Slider; } }
The role function should be reimplemented by all objects and describes the role of themselves and the children that do not provide accessible interfaces of their own.
Next, the accessible interface needs to return the states that the slider can be in. We look at parts of the state() implementation to show how just a few of the states are handled:
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; }
The superclass implementation of state(), uses the QAccessibleInterface::state() implementation. We simply need to disable the buttons if the slider is at its minimum or maximum.
We have now exposed the information we have about the slider to the clients. For the clients to be able to alter the slider - for example, to change its value - we must provide information about the actions that can be performed and perform them upon request. We discuss this in the next section.
QAccessible provides a number of Actions that can be performed on request from clients. If an accessible object supports actions, it should reimplement the following functions from QAccessibleInterface:
Note that a client can request any action from an object. If the object does not support the action, it returns false from doAction().
None of the standard actions take any parameters. It is possible to provide user-defined actions that can take parameters. The interface must then also reimplement userActionCount(). Since this is not defined in the MSAA specification, it is probably only useful to use this if you know which specific AT-Clients will use the application.
QAccessibleInterface gives another technique for clients to handle accessible objects. It works basically the same way, but uses the concept of methods in place of actions. The available methods are defined by the QAccessible::Method enum. The following functions need to be reimplemented from QAccessibleInterface if the accessible object is to support methods:
The action mechanism will probably be substituted by providing methods in place of the standard actions.
To see examples on how to implement actions and methods, you could examine the QAccessibleObject and QAccessibleWidget implementations. You might also want to take a look at the MSAA documentation.
In this section we will explain the procedure of implementing accessible plugins for your interfaces. A plugin is a class stored in a shared library that can be loaded at run-time. It is convenient to distribute interfaces as plugins since they will only be loaded when required.
Creating an accessible plugin is achieved by inheriting QAccessiblePlugin, reimplementing keys() and create() from that class, and adding one or two macros. The .pro file must be altered to use the plugin template, and the library containing the plugin must be placed on a path where Qt searches for accessible plugins.
We will go through the implementation of SliderPlugin, which is an accessible plugin that produces interfaces for the QAccessibleSlider we implemented in the QAccessibleWidget Example. We start with the key() function:
QStringList SliderPlugin::keys() const { return QStringList() << "QSlider"; }
We simply need to return the class name of the single interface our plugin can create an accessible interface for. A plugin can support any number of classes; just add more class names to the string list. We move on to the create() function:
QAccessibleInterface *SliderPlugin::create(const QString &classname, QObject *object) { QAccessibleInterface *interface = 0; if (classname == "QSlider" && object && object->isWidgetType()) interface = new AccessibleSlider(classname, static_cast<QWidget *>(object)); return interface; }
We check whether the interface requested is for the QSlider; if it is, we create and return an interface for it. Note that object will always be an instance of classname. You must return 0 if you do not support the class. updateAccessibility() checks with the available accessibility plugins until it finds one that does not return 0.
Finally, you need to include macros in the cpp file:
Q_EXPORT_STATIC_PLUGIN(SliderPlugin) Q_EXPORT_PLUGIN2(acc_sliderplugin, SliderPlugin)
The Q_EXPORT_PLUGIN2 macro exports the plugin in the SliderPlugin class into the acc_sliderplugin library. The first argument is the name of the plugin library file, excluding the file suffix, and the second is the class name. For more information on plugins, consult the plugins overview document.
You can omit the the first macro unless you want the plugin to be statically linked with the application.
If you do not want to provide plugins for your accessibility interfaces, you can use an interface factory (QAccessible::InterfaceFactory), which is the recommended way to provide accessible interfaces in a statically-linked application.
A factory is a function pointer for a function that takes the same parameters as QAccessiblePlugin's create() - a QString and a QObject. It also works the same way. You install the factory with the installFactory() function. We give an example of how to create a factory for the SliderPlugin class:
QAccessibleInterface *sliderFactory(const QString &classname, QObject *object) { QAccessibleInterface *interface = 0; if (classname == "QSlider" && object && object->isWidgetType()) interface = new SliderInterface(classname, static_cast<QWidget *>(object)); return interface; } int main(int argv, char **args) { QApplication app(argv, args); QAccessible::installFactory(sliderFactory); ... }
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. | Qt 4.4 | |
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD. | ||
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP ! |
Copyright © 2000-2012 - www.developpez.com