Document Viewer

A Widgets application to display and print Json, text and PDF files. Demonstrates various features to use in widget applications: Using QSettings, query and save user preferences, manage file histories and control cursor behavior when hovering over widgets.

Document Viewer demonstrates how to use a QMainWindow with static and dynamic tool bars, menus and actions.

The MainWindow class provides a common application screen with general menus, actions and a tool bar. It provides functionality to open a file, determine the content type and keep a list of previously opened files. It stores information in QSettings and reads settings when launched. Depending on the opened file's content type, it creates a suitable viewer to display it and provide printing functionality.

Creating an executable

To create an executable, we use a standard main.cpp file. First, we set the application's organization name:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QApplication::setOrganizationName(QApplication::translate("main", "QtExamples"));

Creating an application and showing the main window

    QApplication app(argc, argv);
    QApplication::setOrganizationName(QApplication::translate("main", "QtExamples"));
    QApplication::setApplicationName(QApplication::translate("main", "DocumentViewer"));
    QApplication::setApplicationVersion("1.0");

    QCommandLineParser parser;
    parser.setApplicationDescription(QApplication::translate("main",
                                                     "A viewer for JSON, PDF and text files"));
    parser.addHelpOption();
    parser.addVersionOption();
    parser.addPositionalArgument("File", QApplication::translate("main",
                                                                 "JSON, PDF or text file to open"));
    parser.process(app);

    const QStringList &positionalArguments = parser.positionalArguments();
    const QString &fileName = (positionalArguments.count() > 0) ? positionalArguments.at(0)
                                                                : QString();

    MainWindow w;
    w.show();
    if (!fileName.isEmpty())
        w.openFile(fileName);

    return app.exec();
}

The MainWindow class

The class constructor initializes the user interface created in Qt Designer. It links the actions for opening a file and the about dialog to their implementation.

    ui->setupUi(this);
    readSettings();
}

MainWindow::~MainWindow()
{
    saveSettings();
}

void MainWindow::on_actionOpen_triggered()

The mainwindow.ui file provides a QTabWidget on the left, where bookmarks and thumbnails can be displayed. It provides a QScrollArea on the right, where the viewed file contents are displayed.

The ViewerFactory class provides a static method to create a file type specific viewer.

m_viewer = ViewerFactory::makeViewer(&file, ui->viewArea, this, questions());

If the application settings contain a section for the viewer, it is passed to the viewer's virtual restoreState method. Afterwards, the standard UI assets are passed to the viewer and it's display widget is displayed in the main scroll area.

    m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
    ui->scrollArea->setWidget(m_viewer->widget());
}

The ViewerFactory class

The only static method of the class takes a file, the widget where the viewed content is to be displayed, the main window and the user questions. Depending on the file's mime type, it creates an appropriate document viewer.

    connect(m_viewer.get(), &AbstractViewer::printingEnabledChanged, ui->actionPrint, &QAction::setEnabled);
    connect(ui->actionPrint, &QAction::triggered, m_viewer.get(), &AbstractViewer::print);
    connect(m_viewer.get(), &AbstractViewer::showMessage, statusBar(), &QStatusBar::showMessage);

    m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
    ui->scrollArea->setWidget(m_viewer->widget());
}

The AbstractViewer class

The class provides a generalized API to view and browse through a document, save and print it. Properties of the document and the viewer itself can be queried: Does the document have content? Has it been modified? Is an overview (thumbnails or bookmarks) supported? The viewer's state can be saved to and restored from a QByteArray, which the application can access to store in its settings.

AbstractViewer provides protected methods for classes inheriting from it, to create actions and menus on the main window. In order to display these assets on the main window, they are parented to it. AbstractViewer takes responsibility to remove and destroy the UI assets it created. It inherits from QObject to provide access to signals and slots.

Signals
void uiInitialized();

The signal is emitted when AbstractViewer has received all necessary information about UI assets on the main window.

void printingEnabledChanged(bool enabled);

This signal is emitted when document printing has been enabled or disabled, e.g. because a new document has been successfully loaded or all content has been removed.

void printStatusChanged(AbstractViewer::PrintStatus status);

When printing has been started, this signal notifies about the printing progress.

void documentLoaded(const QString &fileName);

The signal notifies the application that a document has been loaded successfully.

The TextViewer class

A simple text viewer, inheriting from AbstractViewer. It features editing text files, copy/cut and paste, printing and saving changes.

The JsonViewer class

The class displays a JSON file in a QTreeView. It loads a file into a QJsonDocument, used to populate a custom tree model with JsonItemModel. This part of the JSON viewer demonstrates, how to implement custom item models inheriting from QAbstractItemModel. The JsonTreeItem class provides basic API for manipulating JSON data and propagating it back to the underlying QJsonDocument.

JsonViewer uses the toplevel objects as bookmarks for navigation. Other nodes (keys or values) can be added as additional bookmarks or removed from the bookmark list. A QLineEdit is used as a search field to navigate through the JSON tree.

The PdfViewer class

This is a fork of the QPdfViewerWidgets example. It demonstrates the use of QScroller to smoothly flick through the document.

Additional classes for application features

The HoverWatcher class

The class can be used to set override cursors when the mouse is hovering over a widget and to restore them upon departure. In order to prevent multiple HoverWatcher instances created for the same widget, it is implemented as a singleton per widget.

HoverWatcher inherits from QObject and the QWidget watched becomes the instance's parent. An event filter is used to intercept the hover events without consuming them.

HoverWatcher::HoverWatcher(QWidget *watched)
    : QObject(watched), m_watched(watched)
{
    Q_ASSERT(watched);
    m_cursorShapes[Entered].emplace(Qt::OpenHandCursor);
    m_cursorShapes[MousePress].emplace(Qt::ClosedHandCursor);
    m_cursorShapes[MouseRelease].emplace(Qt::OpenHandCursor);
    // no default for Left => restore override cursor
    m_watched->installEventFilter(this);
}

The actions watched are represented in an enum.

    enum HoverAction {
        Entered,
        MousePress,
        MouseRelease,
        Left,
        Ignore
    };

Static methods create watchers, check their existence for a specific QWidget or dismiss a watcher.

    static HoverWatcher *watcher(QWidget *watched);
    static const HoverWatcher *watcher(const QWidget *watched);
    static bool hasWatcher(QWidget *widget);
    static void dismiss(QWidget *watched);

A cursor shape can be specified or unset for each HoverAction. If no cursor shape is specified for an action, the application's override cursor will be restored when it occurs.

public slots:
    void setCursorShape(HoverAction type, Qt::CursorShape shape);
    void unSetCursorShape(HoverAction type);

The mouseButtons property specifies, which mouse buttons to consider for the MousePress action.

    void setMouseButtons(Qt::MouseButtons buttons);
    void setMouseButton(Qt::MouseButton button, bool enable);

Action specific signals are emitted when an action has been processed.

signals:
    void entered();
    void mousePressed();
    void mouseReleased();
    void left();

A general signal is emitted which passes the processed action as an argument.

void hoverAction(HoverAction action);

Example project @ code.qt.io

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.