Qt Qt Quarterly
Qt Development Frameworks | Documentation | Qt Quarterly

Using QtHelp to Lend a Helping Hand

by David Boddie

Just as libraries like Qt are not complete without reference guides and API documentation, end user applications need to be distributed with manuals and online help. In their quest to assist with all aspects of the development experience, the developers of Qt's tools have provided a solution that you can use to keep your users informed.

Qt 4.4 introduced so many new features for a minor release that one of them may have escaped many people's notice. The QtHelp module replaces the QtAssistant module of previous versions by providing a mechanism to integrate the Qt Assistant application into applications, but it provides so much more than this.

In this article, we aim to cover some of the new features of the QtHelp module by providing online help for a simple application that happens to feature a scripting interface. As a result, we need to create some API documentation in addition to a user manual, so we will begin by looking at this problem.

Getting Started with API Documentation

The example we use in this article is a simple image processing application that exposes a single class, ImageWrapper, to Qt Script so that users can write small scripts to process images. All the source and header files for the example can be found in the imageprocessor directory.

Although the class isn't provided in a library, we can document it in just the same way that we document a library like Qt. For simplicity, we only document this class, inserting basic documentation in C-style comments like the following:

/**
    Scales the image by the horizontal and vertical
    factors specified by \a xScale and \a yScale
    respectively.
    If \a publish is true, the imageChanged() signal will
    be emitted to notify other components that the image
    has been changed.
*/

void ImageWrapper::scale(qreal xScale, qreal yScale,
                         bool publish)
{
    image = image.scaled(int(xScale*image.width()),
                         int(yScale*image.height()));
    if (publish)
        emit imageChanged(image);
}

To convert this to a form that users can read, we use Doxygen, an open source tool much like the tool used to generate Qt's HTML documentation, but first we must configure it so that it knows which classes to document. Fortunately, Doxygen is supplied with a tool called doxywizard that guides us through the process.

The Doxygen Project Wizard in use.

We click the Wizard... button to begin the process of entering the name of our project, the location of our source files and the output directory for the generated HTML documentation — we choose to place the output within the doc directory. The tabs contain options that we can tweak to customize the output, but we prefer to switch off many of the extra features.

The tool lets us save the configuration — we specify a file called Doxyfile in the example's doc directory — and allows us to select a working directory before running Doxygen itself. Some developers may prefer to run Doxygen at the command line from within the doc directory in the following way:

    doxygen Doxyfile

In our case, Doxygen creates an html directory within the doc directory and places the documentation in there. Users can read this with a regular Web browser, but we also want to make this available from within the application itself, and this means that we first need to make it available to Qt Assistant.

HTML Files and Help Projects

In versions of Qt earlier than Qt 4.4, Qt Assistant worked with collections of HTML files and index files, like qt.dcf which described the contents of the Qt Reference Documentation. In Qt 4.4 and later, Qt Assistant uses a different file format for its help files, based on SQLite database files.

The toolchain used to build Qt's documentation performs conversions between lots of different files with almost identical suffixes. The table below describes what each of them are used for.

SourceBinaryDescription
.qhp.qch Contains a table of contents, an index of items in the documentation, and a file manifest.
.qhcp.qhc Contains information that is used to customize the appearance and available features of Qt Assistant.

To create documentation to be shown in Qt Assistant, we would write a Qt Help Project file (an XML file with a .qhp suffix) and "compile" it to a binary Qt Compressed Help file (with a .qch suffix) by using the qhelpgenerator tool. Doxygen is able to generate .qhp files, but it can also generate .qch files automatically, allowing us to create files that we can use directly with Qt Assistant.

We use the latest version of Doxygen from the project's Subversion repository to create HTML files:

https://doxygen.svn.sourceforge.net/svnroot/doxygen/trunk

You will need to check this out into a new directory, configure it, and build it. We could use a package for Doxygen 1.5.7.1, but the latest sources contain several useful improvements.

For our documentation set, we need to change the Doxyfile configuration file and run Doxygen again. We locate the lines containing GENERATE_QHP, QCH_FILE and other related definitions, and we modify them to look like the following:

    GENERATE_QHP       = yes
    QHP_NAMESPACE      = "com.trolltech.qq.imageprocessor"
    QHP_VIRTUAL_FOLDER = "imageprocessor-0.1"
    
    QCH_FILE = "../../imageprocessor/help/imageprocessor.qch"
    QHG_LOCATION       = "qhelpgenerator"

Now, we run Doxygen as described above to produce a .qch file. The doc directory contains an html subdirectory as before, but an imageprocessor.qch file has been created within the imageprocessor/help directory — the QCH_FILE declaration contains a path relative to the output directory.

The imageprocessor.qch file must be registered with Qt Assistant before its documentation set can be viewed. You can either open Assistant's Edit"|"Preferences... dialog and add the file to the list of documentation sets or, in the imageprocessor directory, simply type the following at the command line:

    assistant -register help/imageprocessor.qch

Since the HTML files, indexes and table of contents are all contained in this one file, you can install it where you like.

Although we don't define them in our example, Doxygen can also be configured to create custom filters for the documentation, as described in the QtHelp module documentation. The definitions for this feature use the following configuration variables:

    QHP_CUST_FILTER_NAME
    QHP_CUST_FILTER_ATTRS
    QHP_SECT_FILTER_ATTRS

The Doxyfile included with the example code for this article should produce everything that we need for our application.

Profiles and Streamlining Documentation

One way to provide access to our documentation is to open Qt Assistant from within our application and control it using its remote control feature. This is covered by the Simple Text Viewer Example that is distributed with Qt. You can get a feel for the possibilities of this approach by registering the imageprocessor.qch file with Assistant, as described above, and invoking it with the -enableRemoteControl option:

    assistant -enableRemoteControl
    hide contents;
    hide index;
    hide bookmarks;
    hide search;
    setSource qthelp://com.trolltech.qq.imageprocessor/imageprocessor-0.1/index.html;

We can customize the user interface and available documentation sets by using a Qt Help Collection (.qhc) file. This is the preferred way to use Qt Assistant as a help viewer for an application because it ensures that only the relevant help is shown. Once again, we refer the reader to the Simple Text Viewer Example for information on using .qhc files with Assistant.

For deeper integration, we turn to the classes in the QtHelp module itself, and here we need to create a .qhc file before we can proceed. To do this, we write a Qt Help Collection Project (.qhcp) file which will be compiled to give us a help collection.

The imageprocessor.qhcp file looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <QHelpCollectionProject version="1.0">
    <assistant>
      <startPage>qthelp://com.trolltech.qq.imageprocessor/
    imageprocessor-0.1/classImageWrapper.html</startPage>
    </assistant>
    <docFiles>
      <register>
      <file>../imageprocessor/help/imageprocessor.qch</file>
      </register>
    </docFiles>
    </QHelpCollectionProject>

Many additional settings can be added to customize the collection for use with Qt Assistant, but these are not relevant if it is going to be used directly in our application. A compiled collection file can be created with the qcollectiongenerator tool, supplied with Qt:

    qcollectiongenerator doc/imageprocessor.qhcp \
                      -o doc/imageprocessor.qhc

You can test the collection by running Qt Assistant with the -collectionFile option:

    assistant -collectionFile doc/imageprocessor.qhc

This should show whether or not the collection is correct.

Integrating Documentation into the Application

We would like to be able to give users access to documentation from within the user interface of our application. To do this, we first need to set up the help system and load the imageprocessor.qhc help collection file. We also need to display the HTML in a browser — for convenience, we simply subclass QTextBrowser and extend it with functionality to access the help collection.

The Image Processor example with built in documentation.

Let's take a look at the code to set up the help engine and lay out the user interface, which we execute in the constructor of our application's main window:

    QHelpEngine *helpEngine = new QHelpEngine(DOCS_PATH,
                                              this);
    helpEngine->setupData();

The DOCS_PATH macro is something that we define in the build system for the application — it contains the installed location of the imageprocessor.qhc file. The help engine loads this file, and we ensure it is set up before use.

The rest of this code creates a dock widget that contains a table of contents, provided as a QHelpContentWidget by the help engine, and a custom text browser widget which is "help system aware".

    helpWindow = new QDockWidget(tr("Help"), this);
    QSplitter *helpPanel = new QSplitter(Qt::Horizontal);
    HelpBrowser *helpBrowser = new HelpBrowser(helpEngine);
    
    helpPanel->insertWidget(0, helpEngine->contentWidget());
    helpPanel->insertWidget(1, helpBrowser);
    helpPanel->setStretchFactor(1, 1);
    helpWindow->setWidget(helpPanel);
    addDockWidget(Qt::BottomDockWidgetArea, helpWindow);
    ...
    connect(helpEngine->contentWidget(),
            SIGNAL(linkActivated(const QUrl &)),
            helpBrowser, SLOT(setSource(const QUrl &)));

We connect these two widgets via the linkActivated() signal so that the user can view topics by double clicking them in the table of contents.

The help browser is simply a subclass of QTextBrowser that can handle the qthelp:// URLs used by the help engine. The relevant part of its implementation is the loadResource() function, which uses the QHelpEngine instance passed on construction to retrieve data for the browser.

    QVariant HelpBrowser::loadResource(int type,
                                       const QUrl &url)
    {
        if (url.scheme() == "qthelp")
            return QVariant(helpEngine->fileData(url));
        else
            return QTextBrowser::loadResource(type, url);
    }

For all other URLs, it simply uses the default implementation provided by QTextBrowser.

In the example code accompanying this article, we have put together a simple build system that generates the documentation using Doxygen and creates a help collection with Qt's qcollectiongenerator tool.

The imageprocessor.qch and imageprocessor.qhc files produced by this process are "installed" within the source directory for the example application, and it is this location that is passed as the DOCS_PATH macro to the compiler when building the application.

Adding User Guides and Online Help

Although Doxygen is primarily used for API documentation, it can also be used to create manual pages that can be included in the help collection along with the scripting documentation. Alternatively, we could use a different set of tools to generate HTML documentation and create a separate help collection; QHelpEngine is designed to handle multiple collections, so we would just need a way to present both tables of contents together.

The help engine provides mechanisms to allow individual pages of documentation to be referenced by their "qthelp://" URLs, but it also exposes a search engine, powered by the CLucene indexing library, which can itself be made available to users via the QHelpSearchQueryWidget and QHelpSearchResultWidget classes. Developers typically obtain these widgets via the default QHelpSearchEngine instance provided by the help engine and insert them into the user interface in the same way we used with the QHelpContentWidget instance.

Having the underlying models for the table of contents and documentation index can also be useful for applications that need to expose documents for specific features via certain user interface elements, such as menu items and toolbar buttons. The application can respond to these when they are activated or pressed and retrieve the data directly from the help engine.

More specialized uses of the help system could involve using help collections to store text for tooltips and other forms of online help — with enough time to experiment, plenty of ways can be found to use this source of indexed data.

Further Reading and Resources

The following links should help you get started with the process of documenting your own libraries and applications.

Doxygen can be obtained from the project's Web site:

http://www.stack.nl/~dimitri/doxygen/

The latest version of Doxygen contains code to generate help project and compressed help files as a result of work done on the now-obsolete doxygen2qthelp tool, described in a Qt Labs blog from earlier in the year:

http://labs.trolltech.com/blogs/2008/06/20/

A good place to start reading about the QtHelp module is its page in the Qt documentation:

http://doc.trolltech.com/4.4/qthelp.html

The source code for the example described in this article can be obtained from the Qt Quarterly Web site.

Thanks to Sebastian Pipping for his last minute corrections and suggestions for this article and example code.

Site Map Accessibility Contact