Вывод справки в html формате

      Комментарии к записи Вывод справки в html формате отключены

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 4 мес., 1 неделя назад.

  • Автор
    Сообщения
  • #3161

    Какое бы приложение Вы ни разрабатывали, рано или поздно Вам потребуется написать рекомендации по его использованию для пользователя. Есть много способов это сделать, но не все они хорошо подойдут для мобильного приложения. Я сталкивался с разработкой страницы помощи для пользователя два раза — при разработке игрушек.

    QTextBrowser с пролистыванием содержимого пальцем

    Первый раз (логическая игра про ежика), я описал правила игры, при этом я использовал стандартный QTextBrowser — он умеет отображать html код и это очень удобно. Однако если текст не умещается на страницу (в окно виджета), то появится скроллбар, которым не удобно пользоваться на телефоне. По этой причине в тот раз я максимально сократил описание игры и добавил демо (игру с подсказками). Чтобы создать демо-игру я добавил наследника от класс игрового уровня с новым типом игровых элементов (в игре были тучки, по которым бегал ежик) и вывод подсказок. Подробное описание демо не входит в эту статью (посмотреть реализацию можно в репозитории), важно лишь то, что QTextBrowser может быть не так хорош на телефоне.

    Во второй раз я решил вывести большую справку по игре, при этом сделать удобное пролистывание на телефоне. Класс QTextBrowser нас вполне устраивает в плане отображения html-кода (хотя он не умеет, например, обрабатывать размер изображения, заданный в процентах от ширины страницы). В связи с этим мы создадим класс-наследник, в котором добавим возможность прокрутки содержимого мышкой (пальцем на телефоне). Для этого определим в нашем классе методы mousePressEvent, mouseMoveEvent и MouseReleaseEvent. Кроме того, нам потребуются флажок, показывающий происходит ли в данный момент прокрутка и переменная, отвечающая за текущее положение курсора:

    #include <QTextBrowser>
    
    class TutorialViewer : public QTextBrowser {
        Q_OBJECT
    public:
        TutorialViewer(QWidget* parent = 0);
    protected:
        void mousePressEvent(QMouseEvent* event);
        void mouseMoveEvent(QMouseEvent* event);
        void mouseReleaseEvent(QMouseEvent*);
    private:
        int m_cursorPos_y;
        bool m_isMouseButtonPressed;
    }; 

    В конструкторе, помимо вызова конструктора базового класса, выполняется инициализация данных-членов:

    TutorialViewer::TutorialViewer(QWidget* parent)
        : QTextBrowser(parent), m_cursorPos_y(0), m_isMouseButtonPressed(false) {
    }

    При нажатии мыши необходимо выставить флажок и запомнить текущую позицию:

    void TutorialViewer::mousePressEvent(QMouseEvent* event) {
        m_cursorPos_y = event->pos().y();
        m_isMouseButtonPressed = true;
    }

    При перемещении мыши нужно игнорировать событие, если флажок не выставлен (не происходит перетаскивание). Если же в настоящее время пользователь тащит содержимое виджета, то нужно определить расстояние по вертикали между текущим положением мыши и предыдущим (m_cursorPos_y), затем получить текущее положение скроллбара и изменить его на вычисленное расстояние:

    void TutorialViewer::mouseMoveEvent(QMouseEvent* event) {
        if (m_isMouseButtonPressed == false)
            return;
    
        int distance = event->pos().y() - m_cursorPos_y;
    
        QScrollBar* vScroll = this->verticalScrollBar();
        if (vScroll == nullptr)
            return;
    
        int scrollValue = vScroll->value();
    
        vScroll->setValue(scrollValue - distance);
        m_cursorPos_y = event->pos().y();
    }

    Обратите внимание, что если перетаскивание не происходит я сразу выполняю return, т.е. событие не будет обработано ни нашим классом, ни базовым. В данном случае это работает, но в других случаях, вам возможно стоит передать событие на обработку в базовый класс (выполнить QTextBrowser::mouseMoveEvent(event)).

    Наконец, если кнопка мыши отпущена, нам нужно снять флажок:

    void TutorialViewer::mouseReleaseEvent(QMouseEvent* ) {
        m_isMouseButtonPressed = false;
    }

    Встраивание виджета в приложение

    Мы сделали виджет, который умеет отображать html-код и прокручиваться пальцем, но нам нужен полноценный виджет со справкой (кнопкой возврата в предыдущий экран и еще чем-нибудь). Верстать такие виджеты удобно в Qt Designer — это делается с помощью мыши и посмотреть результат (хотя бы частично) вы можете без компиляции приложения. Подробно про работу с Qt Designer можно прочитать прочитать в статье: Собственные виджеты в Qt Designer, а сейчас я опишу лишь порядок действий, который даст мне нужный результат.

    Нам нужно добавить в проект класс формы Qt Designer — автоматически будут созданы файлы .h и .cpp, содержащие класс и файл .ui, с формой, которую можно редактировать мышкой.

    Я добавил на форму объект типа QTextBrowser (т.к. его наследует наш виджет справки) и QHBoxLayout. На layout я добавил кнопку (возврата в предыдущий экран) и два horizontalSpacer чтобы кнопка была по центру виджета. Затем, щелкнул на форму правой кнопкой и выбрал компоновку по вертикали. Получилась следующая форма:
    qt-designer-using
    Однако, на форме сейчас находится экземпляр стандартного класса QTextBrowser, а не нашего TutorialViewer. Чтобы изменить это, нужно преобразовать этот виджет в наш — щелкнем по нему правой кнопкой мыши и выберем в меню «Преобразованные виджеты». В появившемся окне введем имя нашего виджета и путь до его заголовочного файла:
    qt-designer-conversion-widget
    Теперь на форме лежит экземпляр нашего класса, работать мы можем с ним также как с QTextBrowser — двойным щелчком можно открыть окно для изменения содержимого.
    Я ввел текст справки, а для вставки картинок перешел в режим редактирования html и добавил туда картинки в виде: <img src=":/images/menu.png"/>
    Предварительно я добавил в проект файл ресурсов и в него (щелчками мыши) вставил картинки со скриншотами. Такие пути к файлам, как вы видели выше можно скопировать из файла ресурсов.
    В итоге я получил следующий вид виджета в Qt Designer:
    qt-designer-using-custom-widget

    В классе TutorialScreen, который был добавлен автоматически при создании класса формы, я вставил сигнал back(), который будет вырабатываться при нажатии кнопки «назад»:

    #include <QWidget>
    
    namespace Ui {
        class TutorialScreen;
    }
    
    class TutorialScreen : public QWidget {
        Q_OBJECT
    public:
        explicit TutorialScreen(QWidget *parent = 0);
        ~TutorialScreen();
    signals:
        void back();
    private:
        Ui::TutorialScreen *ui;
    };

    В файле реализации я лишь соединил сигнал clicked() от кнопки, расположенной на форме с сигналом класса (да, в Qt мы можете соединять не только сигналы со слотами, но и сигналы с сигналами):

    #include "tutorialscreen.h"
    #include "ui_tutorialscreen.h"
    
    TutorialScreen::TutorialScreen(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::TutorialScreen) {
        ui->setupUi(this);
    
        connect(ui->back, SIGNAL(clicked(bool)), SIGNAL(back()));
    }
    
    TutorialScreen::~TutorialScreen() {
        delete ui;
    }

    В функции main() остается лишь создать экземпляр класса TutorialScreen и вызвать для него метод show(). Однако, мы можете изменить стиль оформления окна при помощи Qt Style Sheets, я писал про это достаточно подробно в статье «Разработка игры на С++, Qt«. Таблицы стилей можно задать к отдельным виджетам в коде, прямо в Qt Designer (так я делал раньше), или ко всему приложению (при этом вы можете использовать синтаксис для задания стилей к иерархиям виджетов):

    #include "tutorialscreen.h"
    #include <QApplication>
    #include <QDebug>
    
    int main(int argc, char *argv[]) {
      try {
        QApplication a(argc, argv);
    
        a.setStyleSheet(
            "QPushButton { "
                "background-color: rgba(255, 153, 102, 200); "
                "border-style: outset;"
                "border-width: 2px;"
                "border-radius: 10px;"
                "border-color: beige;"
                "font: bold 14px;"
                "padding: 6px;"
                "font-size:14pt;"
            "}"
            "QPushButton:hover {"
                "background-color: rgba(255, 102, 0, 200);"
            "}"
            "QPushButton:pressed {"
                "background-color: rgba(255, 0, 0, 200);"
            "}"
            "QPushButton:disabled {"
                "background-color: rgba(204, 153, 102, 200);"
            "}"
            "TutorialScreen > TutorialViewer {"
                "background-color: rgba(139, 137, 112, 50); "
            "}"
        );
    
        TutorialScreen screen;
        screen.show();
    
        return a.exec();
      }
      catch (QString err) {
        qDebug() << err;
      }
    }
    

    В результате я получил следующее окно справки:
    qss-using-qt
    Вертикальный скролл QTextEdit я убрал в настройках виджета в Qt Designer, поэтому скролл не отображается, но содержимое можно проматывать колесиком мыши (удобно на десктопе) и пальцем (на телефоне). Кнопка возврата в предыдущий экран сейчас не работает (хотя сигнал back() вырабатывается). В следующей заметке я расскажу как красиво обработать такие сигналы.

    Пример программы из статьи вы можете взять из репозитория.

Для ответа в этой теме необходимо авторизоваться.