Using QTextBrowser as a Simple Help Engine

Large and sophisticated applications may require more online help than tooltips, status tips, and "What's This?" help can provide. A simple solution to this is to provide a help browser. Applications that provide a help browser typically have a Help entry in the main window's Help menu and a Help button in every dialog.

In this section, we present the simple help browser shown in Figure 16.3 and explain how it can be used within an application. The window uses a QTextBrowser to display help pages that are marked up with an HTML-based syntax. QTextBrowser can handle a lot of simple HTML tags, so it is ideal for this purpose.

Figure 16.3. The HelpBrowser widget

We begin with the header file:

#include class QPushButton; class QTextBrowser; class HelpBrowser : public QWidget { Q_OBJECT public: HelpBrowser(const QString &path, const QString &page, QWidget *parent = 0, const char *name = 0); static void showPage(const QString &page); private slots: void updateCaption(); private: QTextBrowser *textBrowser; QPushButton *homeButton; QPushButton *backButton; QPushButton *closeButton; };

The HelpBrowser provides a static function that can be called from anywhere in the application. This function creates a HelpBrowser window and shows the given page.

Here's the beginning of the implementation:

#include #include #include #include #include "helpbrowser.h" HelpBrowser::HelpBrowser(const QString &path, const QString &page, QWidget *parent, const char *name) : QWidget(parent, name, WGroupLeader | WDestructiveClose) { textBrowser = new QTextBrowser(this); homeButton = new QPushButton(tr("&Home"), this); backButton = new QPushButton(tr("&Back"), this); closeButton = new QPushButton(tr("&Close"), this); closeButton->setAccel(tr("Esc")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QHBoxLayout *buttonLayout = new QHBoxLayout(mainLayout); buttonLayout->addWidget(homeButton); buttonLayout->addWidget(backButton); buttonLayout->addStretch(1); buttonLayout->addWidget(closeButton); mainLayout->addWidget(textBrowser); connect(homeButton, SIGNAL(clicked()), textBrowser, SLOT(home())); connect(backButton, SIGNAL(clicked()), textBrowser, SLOT(backward())); connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(textBrowser, SIGNAL(sourceChanged(const QString &)), this, SLOT(updateCaption())); textBrowser->mimeSourceFactory()->addFilePath(path); textBrowser->setSource(page); }

The layout is simply a row of buttons above a QTextBrowser. The path parameter is a path in the file system that contains the application's documentation. The page parameter is the name of the documentation file, with an optional HTML anchor.

We use the WGroupLeader flag because we want to pop up HelpBrowser windows from modal dialogs in addition to the main window. Modal dialogs normally prevent the user from interacting with any other window in the application. However, after requesting help, the user must obviously be allowed to interact with both the modal dialog and the help browser. Using the WGroupLeader flag makes this interaction possible.

void HelpBrowser::updateCaption() { setCaption(tr("Help: %1").arg(textBrowser->documentTitle())); }

Whenever the source page changes, the updateCaption() slot is executed. The documentTitle() function returns the text specified in the page's

tag.

void HelpBrowser::showPage(const QString &page) { QString path = qApp->applicationDirPath() + "/doc"; HelpBrowser *browser = new HelpBrowser(path, page); browser->resize(500, 400); browser->show(); }

In the showPage() static function, we create the HelpBrowser window and then show it. The window will be destroyed automatically when the user closes it, since we set the WDestructiveClose flag in the constructor.

For this example, we assume that the documentation is located in the doc subdirectory of the directory containing the application's executable. All the pages passed to the showPage() function will be taken from this doc subdirectory.

Now we are ready to invoke the help browser from the application. In the application's main window, we would create a Help action and connect it to a help() slot that could look like this:

void MainWindow::help() { HelpBrowser::showPage("index.html"); }

This assumes that the main help file is called index.html. For dialogs, we would connect the Help button to a help() slot that could look like this:

void EntryDialog::help() { HelpBrowser::showPage("dialogs.html#entrydialog"); }

Here we look in a different help file, dialogs.html, and scroll the QTextBrowser to the entrydialog anchor.

One other place from which we might want to invoke help is a "What's This?" text. We can link the "What's This?" text to the documentation by using HTML <a href="..."> tags.</a>

<a href="...">Figure 16.4. A "What's This?" text with links</a>

<a href="..."></a>

<a href="...">To make hypertext links work from "What's This?" text, we must use a QWhatsThis that is aware of the help browser. This is accomplished by subclassing QWhatsThis and reimplementing its clicked() function to call HelpBrowser::showPage(). Here's the class definition:</a>

<a href="..."> class MyWhatsThis : public QWhatsThis { public: MyWhatsThis(QWidget *widget, const QString &text); QString text(const QPoint &point); bool clicked(const QString &page); private: QString myText; }; </a>

<a href="...">The text() and clicked() functions are reimplemented from QWhatsThis.</a>

<a href="..."> MyWhatsThis::MyWhatsThis(QWidget *widget, const QString &text) : QWhatsThis(widget) { myText = text; } </a>

<a href="...">The constructor accepts a widget and a "What's This?" text for that widget. We pass on the widget to the base class and store the text in a private variable.</a>

<a href="..."> QString MyWhatsThis::text(const QPoint &) { return myText; } </a>

<a href="...">The text() function returns the "What's This?" text for a widget given a certain mouse cursor position. For some widgets, it might make sense to return a different text depending on where the user clicked on it, but here we always return the same text.</a>

<a href="..."> bool MyWhatsThis::clicked(const QString &page) { if (page.isEmpty()) { return true; } else { HelpBrowser::showPage(page); return false; } } </a>

<a href="...">The clicked() function is called by QWhatsThis when the user clicks on the "What's This?" window. If the user clicked on an HTML link, QWhatsThis passes the target page to the clicked() function. (If anything else is clicked, an empty string is passed.) We invoke the help browser with the given page.</a>

<a href="...">The return value of clicked() is used by QWhatsThis to determine whether it should hide the "What's This?" text (indicated by true) or continue to show it. When the user clicks a link, we want the "What's This?" to stay visible along with the help window, so we return false. If the user clicked elsewhere in the "What's This?" window, we return true to hide the "What's This?" window.</a>

<a href="...">Here's how the MyWhatsThis class can be used:</a>

<a href="..."> new MyWhatsThis(sourceLineEdit, tr("" " The meaning of the " "</a><a href="">Source</a> field depends on " "the <a href="">Type</a> field:" "

"));

Instead of calling QWhatsThis::add(), we create a MyWhatsThis object with the widget and its associated text. But this time, if the user clicks a link, the help browser is invoked.

It may look strange that we allocate an object with new and don't assign the value to a variable. This is not a problem here because Qt keeps track of all QWhatsThis objects and deletes them when they are no longer needed.

Категории