Document Viewer▲
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.
I. 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"
));
II. 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 &
amp;positionalArguments =
parser.positionalArguments();
const
QString &
amp;fileName =
(positionalArguments.count() &
gt; 0
) ? positionalArguments.at(0
)
:
QString();
MainWindow w;
w.show();
if
(!
fileName.isEmpty())
w.openFile(fileName);
return
app.exec();
}
III. 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-&
gt;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(&
amp;file, ui-&
gt;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-&
gt;initViewer(ui-&
gt;actionBack, ui-&
gt;actionForward, ui-&
gt;menuHelp-&
gt;menuAction(), ui-&
gt;tabWidget);
ui-&
gt;scrollArea-&
gt;setWidget(m_viewer-&
gt;widget());
}
IV. 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(), &
amp;AbstractViewer::
printingEnabledChanged, ui-&
gt;actionPrint, &
amp;QAction::
setEnabled);
connect(ui-&
gt;actionPrint, &
amp;QAction::
triggered, m_viewer.get(), &
amp;AbstractViewer::
print);
connect(m_viewer.get(), &
amp;AbstractViewer::
showMessage, statusBar(), &
amp;QStatusBar::
showMessage);
m_viewer-&
gt;initViewer(ui-&
gt;actionBack, ui-&
gt;actionForward, ui-&
gt;menuHelp-&
gt;menuAction(), ui-&
gt;tabWidget);
ui-&
gt;scrollArea-&
gt;setWidget(m_viewer-&
gt;widget());
}
V. 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.
V-1. 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 &
amp;fileName);
The signal notifies the application that a document has been loaded successfully.
VI. The TextViewer class▲
A simple text viewer, inheriting from AbstractViewer. It features editing text files, copy/cut and paste, printing and saving changes.
VII. 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.
VIII. The PdfViewer class▲
This is a fork of the QPdfViewerWidgets example. It demonstrates the use of QScroller to smoothly flick through the document.
IX. Additional classes for application features▲
IX-1. 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-&
gt;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);