Wiki

Accessibility in Qt

by Geir Vattekar

Accessibility in computer software is the practice of making applications usable for people with disabilities. This can be implemented by the application itself — for instance, by using a high-contrast user interface with specially selected colors and fonts — or by providing support for external tools, such as screen readers and Braille displays. In this article, we will implement accessibility support for a custom widget. We will also look at support for assistive tools in Qt software development today.

The generic term for the process of providing support for accessible tools is AT (Assistive Technology). This term covers both the tools (AT-clients) and the applications (AT-servers); we will refer to these as just clients and servers. Although servers can cooperate with clients directly, a separate technology usually participates as a bridge to exchange information between them.

Accessibility in Qt typically takes the form of support for such technologies. Further functionality can be implemented by applications; for instance, using universal design, which integrates accessibility as part of the software design process, which is not common for software design tools.

Qt Accessibility Architecture

Accessibility support in Qt consists of a generic interface, implemented for a technology on each platform: MSAA on Windows, Mac OS X accessibility on the Mac, and Unix/X11 AT-SPI on Linux. Qt's accessibility interface closely follows the MSAA (Microsoft Active Accessibility) standard, which most clients support. Other technologies used by Qt provide similar functionality.

The structure of the user interface is represented as a tree of accessible objects. Qt uses a similar approach by building a tree of QObjects. The QObject tree is traversed, for instance, when painting the user interface (UI). Each node in this accessible tree represents an element in the UI; e.g., a push button, a check box, or a slider handle. There is no direct mapping between accessible objects and QObjects. A slider handle, for instance, is an accessibility object that does not have an equivalent QObject. When the accessible tree is built, each individual QObject is responsible for building a sub-tree that represents it in the accessibility tree.

All accessible objects are subclasses of QAccessibleInterface, which exposes information about objects and receives requests from accessible clients. QAccessible contains enums that define the information available from the object, and the possibilities clients have to alter the object. These are defined as follows:

  • Role: Describes the role the object fills in the user interface; e.g., if it is a main window, a text caret, or a cell in an item view.
  • Action: Describes the actions that clients can perform on the objects; e.g., pushing a button.
  • Relation: Describes the relationship between objects in the object tree. This is used by clients when navigating the tree.
When an assistive tool requests information from a Qt application, it traverses the accessibility tree and queries the accessible objects. In addition to the objects' roles, actions, and relations, information is also available through text properties defined by the Text enum in QAccessible. Examples are Description and Value, which give a general description of the object and its value; e.g., a label text.

Static functions of QAccessible manage the communication between server and clients. They send events to clients on behalf of widgets, build the accessible object tree, and connect to MSAA or one of the other supported technologies.

Implementing Accessibility

Qt implements accessibility for its own widgets. Thus, application developers only need to add support for custom widgets. As an example, we take AnalogClock, a QWidget subclass that displays an analog clock, and we implement an accessible interface for it. The clock can tell clients the current time and notify them when the time changes. It also supports setting the time by moving the hands of the clock. To provide accessibility for our clock widget, we need to implement an accessible interface for it and send accessible events from the widget when the time changes. We will first take a look at the implementation of the interface; here is its header file:

    class AccessibleClock : public QAccessibleWidget
 
    {
    public:
      enum ClockElements {
           ClockSelf = 0,
           HourHand,
           MinuteHand
      };
 
      AccessibleClock(QWidget *widget, Role role = Client,
                      const QString & name = QString());
 
      int childCount() const;
      QRect rect(int child) const;
      QString text(Text text, int child) const;
      Role role(int child) const;
      State state(int child) const;
      int navigate(RelationFlag flag, int entry,
                   QAccessibleInterface **target);
 
      QString actionText(int action, Text t, int child) const;
      bool doAction(int action, int child,
                    const QVariantList &params = 
                    QVariantList());
 
    private:
      AnalogClock *clock() const
        { return qobject_cast<AnalogClock *>(widget()); }
    };

We extend the QAccessibleWidget class. This class inherits from QAccessibleInterface and helps us by handling relationships and navigation to other objects in the accessible tree. It also keeps track of events, states, roles, texts, bounding rectangles, hit testing, and actions common to all widgets. Accessible interfaces for all subclasses of QWidget should use QAccessibleWidget as their base class.

Custom interfaces must implement roles, states, and give appropriate strings for the Text enum. This is implemented by the text(), role(), and state() functions. A widget can support actions by reimplementing actionText() and doAction().

Our clock consists of three accessible objects: the clock itself, as well as the hour and minute hands; we use the enum ClockElements to identify them. The image below shows the accessible sub-tree of the clock with the relations between the accessible objects.

Accessibility Tree

Navigation and Bounding Rectangles

Since the hands have no equivalent objects in the QObject tree (the entire clock is just one widget in Qt), we need to implement navigation and relationships between them and the clock. We do this in navigate(). We also need to give bounding rectangles for them by reimplementing rect().

    QRect AccessibleClock::rect(int child) const
    {
      QRect rect;
 
      switch (child) {
          case ClockSelf:
              rect = clock()->rect();
              break;
          case HourHand:
              rect = clock()->hourHandRect();
              break;
          case MinuteHand:
              rect = clock()->minuteHandRect();
          default:
              return QAccessibleWidget::rect(child);
      }
      QPoint topLeft = clock()->mapToGlobal(QPoint(0,0));
      return QRect(topLeft.x() + rect.x(),
                   topLeft.y() + rect.y(),
                   rect.width(), rect.height());
    }

Notice that the accessible interface's pixel resolution coincides with the widget's; i.e., we can simply call the QWidget::rect() function to calculate the rectangle for the entire clock. If a custom widget is style-aware — it draws its sub-elements with the current QStyle — its accessible interface should use the style to calculate bounding rectangles of sub-elements. Qt's interfaces use the style for this job.

The bounding rectangles of the hands are calculated by AnalogClock whenever it draws itself. We need to map the rectangle to screen coordinates before we return it.

    int AccessibleClock::navigate(RelationFlag flag,
        int entry, QAccessibleInterface **target)
    {
      *target = 0;
 
      if (entry)
        switch (entry) {
          case Child:
            return entry <= childCount() ? entry : -1;
 
          case Left:
          case Right:
            if (entry == MinuteHand || entry == HourHand)
              return entry == MinuteHand ?
                              HourHand : MinuteHand;
            break;
 
          case Down:
            if (entry == ClockSelf)
              return MinuteHand;
            break;
 
          case Up:
            if (entry == MinuteHand || entry == HourHand)
              return ClockSelf;
          default:
            ;
        }
     return QAccessibleWidget::navigate(flag, entry, target);
    }

Clients use navigate() to discover relationships between the interface and objects in its sub-tree. This function returns the child with the specified Relation to the clock widget and points target to its interface. Notice that the accessible interfaces of objects that have no equivalent QObject cannot be acquired because they don't have a separate interface; they are only available through their parent.

We handle the sub-tree of our clock widget, and let QAccessibleWidget handle other navigation. The relationships can be seen in the accessible tree given above.

For clients to navigate our tree correctly, we need to specify the relationships between the objects. The clock is a Controller for the hands, which are in turn Controlled by the clock. These kinds of relationships can be specified by adding a controlling signal. We do this in the constructor of AccessibleClock:

    AccessibleClock::AccessibleClock(QWidget *widget,
        Role role, const QString &name)
        : QAccessibleWidget(widget, role, name)
    {
        addControllingSignal("valueChanged(int)");
    }

Note that the choice of signal is not important.

Object Description and State

An accessible object describes itself through its interface. It provides various text strings to describe itself and give information about its contents. Our objects do not have any special states, so we can safely let QAccessibleWidget handle our states for us. Let's take a look at how roles and text properties are resolved for an object.

    QAccessible::Role AccessibleClock::role(int child) const
    {
      switch (child) {
        case ClockSelf:
          return Clock;
        case HourHand:
        case MinuteHand:
          return Slider;
        default:
          ;
      }
      return QAccessibleWidget::role(child);
    }

Selecting a role for an object involves finding a value from the Role enum that best fits the object's purpose. In the case of our clock widget, we were lucky enough to find the Clock role. The hands behave like dials, which are a kind of slider, so they get the Slider role.

    QString AccessibleClock::text(Text text, int child) const
    {
      if (!widget()->isVisible())
        return QString();
 
      switch (text) {
        case Description:
        case Name:
          switch (child) {
            case ClockSelf:
              return "Analog Clock";
            case HourHand:
              return "Hour Hand";
            case MinuteHand:
              return "Minute Hand";
            default:
              ;
          }

When reimplementing text(), one examines the Text enum and decides which values are relevant to the interface being implemented. For the objects of the clock, we have chosen Name, Description, and Value.

      case Value: {
        QString minutes = QString::number(clock()->
          currentTime().minute());
        QString hours = QString::number(clock()->
 
          currentTime().hour());
 
          switch (child) {
            case ClockSelf:
              return hours + " : " + minutes;
            case HourHand:
              return hours;
            case MinuteHand:
              return minutes;
            default:
              ;
          }
      }

The Value enum constant is used for storing the time. Accessible objects use text to store all information. For instance, QSlider stores its value as an int, while its interface stores text in the same manner as our accessible clock. Note that properties are read-only for most objects. To support the setting of text properties, setText() should also be reimplemented.

Setting the Time

To make it possible for a client to set the time by moving the hands, we provide strings that describe the actions available, and perform them upon request. We have a fixed number of actions recognized by clients; these are defined by the QAccessible::Action enum. It is possible to define custom actions, but clients may not respect them.

    QString AccessibleClock::actionText(int action, Text text,
                                        int child) const
    {
      if (action == Increase && child == MinuteHand)
        if (text == Name || text == Description)
          return "Wind the minute hand one minute forward.";
      if (action == Increase && child == HourHand)
        if (text == Name || text == Description)
          return "Wind the hour hand one hour forward";
      ...
 
      return QAccessibleWidget::actionText(action, text, child);
    }

The two Actions suitable for our purpose are Increase and Decrease — to wind the hands clockwise or anticlockwise. The actionText() function returns a string that describes what the requested action does for the specified child. If the child object does not support the requested action or it does not have a string for the specified Text, the function returns an empty string. Here, we let QAccessibleWidget try to find a string before admitting defeat.

    bool AccessibleClock::doAction(int action, int child,
                                   const QVariantList &params)
    {
      if (!widget()->isEnabled())
        return false;
 
      if (action == Increase) {
        if (child == MinuteHand)
          clock()->addMinutes(1);
        else if (child == HourHand)
          clock()->addHours(1);
      }
 
      if (action == Decrease) {
        if (child == MinuteHand)
          clock()->addMinutes(-1);
        else if (child == HourHand)
          clock()->addHours(-1);
      }
 
      return QAccessibleWidget::doAction(action, child,
                                         params);
    }

In doAction(), we perform the actions we support, which are increasing or decreasing the clock hands. We let QAccessibleWidget handle actions supported by all widgets. Note that we need to check whether the clock is enabled; we cannot rely on clients to respect this.

The Passing of Time

The clock should probably inform its clients about the passing of time. Accessible objects can post events to clients through the static updateAccessibility() function of QAccessible. The available events are described by its Event enum. Since we use the object's value to store the current time, it is only natural that we choose ValueChanged for the event type.

    void AnalogClock::timeout()
    {
        ...
        QAccessible::updateAccessibility(this, 0,
            QAccessible::ValueChanged);
 
        update();
    }

The timeout() function is connected to a QTimer which fires every minute. Actually posting the accessible event is straightforward. The widget is used by Qt to look up or create an interface for the widget.

An Accessible Clock

Current Standards and Technologies

Currently, the availability of accessibility standards varies greatly in software. For the communication between clients and servers, MSAA is the technology most commonly supported by clients. MSAA is infamous for its ambiguous specification, lack of support for UI elements, and general quirkiness; we will look at some implementation issues related to this shortly.

The lack of support for several UI elements, such as labels, results in clients using MSAA in an inconsistent way. The information to clients is given as text assigned to roles; e.g, text displayed and general description for separate UI elements. Some roles conflict and that has resulted in clients using the roles in an inconsistent manner.

MSAA is supported by several of the best known assistive tools — for instance the screen readers, JAWS, Window-Eyes and ZoomText, and the speech engines, Dragon and Narrator. However, because development of some tools began before MSAA was available, and because of the missing features and quirks, clients often employ proprietary solutions. JAWS, for instance, assumes the application was developed with the Win32 API and fetches the window handle of common UI elements; e.g, push buttons and scroll bars. Only if this fails, will JAWS turn to MSAA.

Dealing with Quirks

The lack of standards, the shortcomings of MSAA, and hacks by server and client vendors demand that application developers need to do some hacking and tricks themselves. Often, the applications must be tested against specific clients that should be supported. We will now look at a few examples on how to deal with common problems encountered in Qt application development.

Servers notify clients when a new object receives focus. Often, clients perform a bottom-up traversal of the accessible tree and pass on to the user the descriptive texts of objects able to accept focus. This can cause problems because leaf objects in the Qt object tree that do not accept focus, such as labels, may never be discovered by the client.

Another related problem is knowing whether labels describe other objects. A text field, for instance, often has a label describing it. A user of an assistive client will not know what the field is for if he/she is not informed about the label. Qt solves this in that, if a widget's buddy is a label, it includes the text of the label in the widget's description. A similar technique could be used for labels in leaf nodes.

With MSAA there is no direct function to know how many children a list has. This leaves it up to the individual servers to figure out how to pass this information to clients. Qt appends a description of this to the Description text role.

The level of the current item in a tree cannot be queried. This is due to lazy evaluations. Qt solves this by using the Value text role to store the level of the current tree item.

As mentioned previously, in some cases, more that one text role can be appropriate for the same information. An example is the text of labels, for which Acrobat Reader uses the Value role while Qt widgets and most clients use the Name role.

There can also be ambiguity between states and text roles. We have a static text role, but also a read-only state, both of which can be used for disabled text objects. In Qt, we use the accessible name for label text; for consistency in applications, all widgets that display text should also use the this text role.

MSAA provides support for text carets through a caret role. However, most clients do not use this, but use proprietary solutions; e.g, checking on the screen for a vertical blinking line.

The Future

In a cross-platform framework, such as Qt, the lack of a standard interface respected by assistive tools on the various platforms presents several problems. In particular, it is difficult to find a common abstraction between technologies, and at the same time provide sufficient support for clients. Currently, Qt's implementation closely follows IAccessible, which is the API of the accessible interface of MSAA.

However, a new API, IAccessible2, has been developed to complement and fill the gaps in IAccessible — notable improvements are additional functionality for rich text tables, spreadsheets, and common mainstream applications; e.g., Web 2.0 applications.

Support for the IAccessible2 interface is under development in Qt, but we do not currently have an estimate for when it will be in place. A full discussion of IAccessible2 is therefore best left to a future article or overview.

Summary

The apparent challenge for developers implementing accessibility today is dealing with the quirks and shortcomings of available technologies, and proprietary solutions of client vendors.

We have discussed some remedies with regard to Qt application development. Still, it may be a good idea to test applications with the most common assitive tools. In cases where clients are in conflict, one will just have to choose which client one wishes to support.

Обсудить...