Wiki

Обеспечение контекстно-зависимой помощи

Трентон Шульц

Qt предлагает много способов обеспечения оперативной помощи, которые включают в себя советы, сообщения типа "Что это?" и доступ к программе-помощнику Qt Assistant. Иногда мы хотим обеспечить больше помощи для определенного виджета, чем может предложить сообщение "Что это?", и нам хочется предоставить пользователю помощь, не вынуждая его искать или пролистывать страницу для этого. Решение проблемы - обеспечить контекстно-зависимую помощь для текущего виджета.

В этой статье мы представляем класс HelpClient. Этот класс обеспечивает механизм для соотношения виджетов с файлами помощи. Класс также имеет фильтр события, который он использует, чтобы обнаружить, когда пользователь просит помощи (например, нажатием F1) и обратиться к Qt Assistant с соответствующим файлом помощи. Давайте начнем с определения HelpClient:

typedef QMap<QWidget *, QString>    HelpMap;
 
class HelpClient : public QObject
{
    Q_OBJECT
 
public:
    HelpClient(QObject *parent, const char *name = 0);
    void installHelpFiles(HelpMap helpMap);
 
protected:
    bool eventFilter(QObject *obj, QEvent *event);
 
private slots:
    void handleError(const QString &msg);
 
private:
    QAssistantClient *mClient;
    HelpMap mHelpMap;
};

Код включает оператор typedef для предоставления нам отображения QWidget * -> QString. Класс HelpClient использует класс QAssistantClient, который обеспечивает интерфейс Qt Assistant. QAssistantClient реализован в отдельной библиотекей, так что вы должны добавить следующую строку к вашему .pro файлу таким образом, чтобы ваша система могла найти его:

LIBS += -lqassistantclient

Теперь мы готовы посмотреть на выполнение HelpClient

const QString AssistantPath = "/usr/X11R6/bin";

Чтобы запустить Qt Assistant, мы должны знать, где он находится. Здесь мы указываем путь, но его можно легко найти в QSettings и установить при первой инсталляции приложения.

HelpClient::HelpClient(QObject *parent, const char *name)
    : QObject(parent, name)
{
    parent->installEventFilter(this);
    mClient = new QAssistantClient(AssistantPath, this);
    connect(mClient, SIGNAL(error(const QString&)),
            this, SLOT(handleError(const QString &)));
}

В конструкторе HelpClient устанавливает себя как фильтр события на его родителе. Это означает, что каждое событие, которое отослано родителю, должно сразу пройти через нашу функцию HelpClient::eventfilter(), на которую мы обратим внимаение позже. Остальная часть конструктора имеет дело с инициализацией QAssistantClient. QAssistantClient берет путь к Qt Assistant как его первый аргумент и родитель QObject как его второй аргумент. Как всегда в Qt, QAssistantClient будет удален, когда его родитель удален. Мы также делаем связь между сигналом QAssistantClient's error () и нашим собственным handleError() слотом.

void HelpClient::handleError(const QString &msg)
{
    QMessageBox::information(0, tr("Error"), msg);
}

Эта функция более полезна для отладки.

void HelpClient::installHelpFiles(HelpMap helpMap)
{
    mHelpMap = helpMap;
}

Функция installHelpFiles() простая. Теперь давайте посмотрим на фильтр события, который обеспечивает основу функциональности.

bool HelpClient::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Key_F1 || ke->key() == Key_Help) {
            QWidget *widget = 0;
            QString page = "index.html";
            if (obj->isWidgetType())
                widget = static_cast<QWidget *>(obj)-> focusWidget();
            HelpMap::const_iterator it = mHelpMap.find(widget);
            if (it != mHelpMap.constEnd())
                page = *it;
 
            mClient->showPage(qApp->applicationDirPath() + "/help/" + page);
            return true;
        }
    }
    return false;
}

Функция eventFilter() принимает значение QEvent, и QObject, которому назначено событие. Она возвращает булевское значение, что должно быть сделано с событием по окончании функции.

Начнем с проверки типа события. Если это нажатие клавиши (KeyPress), мы относим его к KeyEvent. Затем мы должны удостовериться, что это клавиша F1 (стандартная клавиша помощи) или Help (специальная клавиша для Mac и Sparc). Если нажата клавиша помощи, мы проверяем, является ли объект, который получит событие, виджетом, и если это так, мы вызываем focuswidget(). Focuswidget() используется как клавиша для вызова нужной страницы из HelpMap. Если подходящего виджета нет, возвращаемся на страницу index.html. В случае, если есть страница для отображения, обращаемся к HelpClient::showPage(),который запускает Qt Assistant с подходящей страницы. Предполагается, что help-файлы приложения находятся в подкаталоге help каталога, где находятся данные приложения.

Обычно мы вызываем accept() или ignore() по событию QKeyEvent, чтобы определить, должно ли распространяться событие, но в фильтре события возвращаем значение true или false, чтобы указать Qt необходимое действие. В случае нажатия клавиши помощи мы возвращаем значение true, чтобы указать Qt, что мы обработали это событие, так что оно не должно передаваться дальше; в противном случае возвращаем значение false, которое заставляет Qt продолжать поиск событий.

Использование HelpClient

Как использовать HelpClient. Мы просто создаем объект HelpClient в каждом виджете, где мы хотим обеспечить помощь, и передаем в HelpMap, которая связывает производные виджеты с help-файлами. Предположим, например, что мы хотим обеспечить помощь для конфигурационного диалога, такого, как приведенный ниже.

Helpclient-Basic

Helpclient-Adv

Чтобы использовать HelpClient, мы просто включаем helpclient.h и создаем HelpMap в конструкторе виджета или функции init().

#include "helpclient.h"
 
void ConfigureForm::init()
{
    HelpClient *helpClient = new HelpClient(this);
    HelpMap hMap;
    hMap.insert(helpButton, "config.html");
    hMap.insert(okButton, "config.html");
    hMap.insert(cancelButton, "config.html");
    hMap.insert(saveCheckBox, "config.html#save");
    hMap.insert(restoreCheckBox, "config.html#restore");
    hMap.insert(clipboardCheckBox, "config.html#clipboard");
    hMap.insert(webButton, "browser.html");
    hMap.insert(emailButton, "email.html");
    helpClient->installHelpFiles(hMap);
}

В случае с флагами (checkbox) мы должны внести информацию-помощь в тот же самый файл и использовать стандартный HTML # синтаксис, (например, ) в help-файле.

Заметьте, что для того, чтобы это хорошо работало, все виджеты должны иметь StrongFocus или WheelFocus. Это гарантирует, что табуляция или щелчок мышью на виджете и нажатие F1 обеспечит контекстно-зависимую помощь.

Предлагаемые улучшения

HelpClient полезен и в том виде, в котором он есть, но есть некоторые возможности, на которые вы могли бы обратить внимание:

  • Настроить функцию setDefaultPage()так, чтобы страницей по умолчанию не всегда была index.html.
  • Вместо того, чтобы реагировать на ошибки самому при помощи handleError(), перенаправить сигнал error() и предоставить возможность программисту решать, как поступить с ошибкой.
  • Вместо того, чтобы вставлять всю информацию в один файл при помощи installHelpFiles(), использовать функцию registerPage(), которая работает с виджетом и строкой.
  • Сделать HelpClient статическим классом, который создается при запуске приложения и доступен во всем приложении.

Copyright © 2003 Trolltech Trademarks