Application Example▲
Nearly all of the code for the Application example is in the MainWindow class, which inherits QMainWindow. QMainWindow provides the framework for windows that have menus, toolbars, dock windows, and a status bar. The application provides File, Edit, and Help entries in the menu bar, with the following popup menus:
The status bar at the bottom of the main window shows a description of the menu item or toolbar button under the cursor.
To keep the example simple, recently opened files aren't shown in the File menu, even though this feature is desired in 90% of applications. Furthermore, this example can only load one file at a time. The SDI and MDI examples show how to lift these restrictions and how to implement recently opened files handling.
MainWindow Class Definition▲
Here's the class definition:
class
MainWindow : public
QMainWindow
{
Q_OBJECT
public
:
MainWindow();
void
loadFile(const
QString &
amp;fileName);
protected
:
void
closeEvent(QCloseEvent *
event) override
;
private
slots:
void
newFile();
void
open();
bool
save();
bool
saveAs();
void
about();
void
documentWasModified();
#ifndef QT_NO_SESSIONMANAGER
void
commitData(QSessionManager &
amp;);
#endif
private
:
void
createActions();
void
createStatusBar();
void
readSettings();
void
writeSettings();
bool
maybeSave();
bool
saveFile(const
QString &
amp;fileName);
void
setCurrentFile(const
QString &
amp;fileName);
QString strippedName(const
QString &
amp;fullFileName);
QPlainTextEdit *
textEdit;
QString curFile;
}
;
The public API is restricted to the constructor. In the protected section, we reimplement QWidget::closeEvent() to detect when the user attempts to close the window, and warn the user about unsaved changes. In the private slots section, we declare slots that correspond to menu entries, as well as a mysterious documentWasModified() slot. Finally, in the private section of the class, we have various members that will be explained in due time.
MainWindow Class Implementation▲
#include <QtWidgets>
#include
"mainwindow.h"
We start by including <QtWidgets>, a header file that contains the definition of all classes in the Qt Core, Qt GUI and Qt Widgets modules. This saves us from the trouble of having to include every class individually. We also include mainwindow.h.
You might wonder why we don't include <QtWidgets> in mainwindow.h and be done with it. The reason is that including such a large header from another header file can rapidly degrade performances. Here, it wouldn't do any harm, but it's still generally a good idea to include only the header files that are strictly necessary from another header file.
MainWindow::
MainWindow()
:
textEdit(new
QPlainTextEdit)
{
setCentralWidget(textEdit);
createActions();
createStatusBar();
readSettings();
connect(textEdit-&
gt;document(), &
amp;QTextDocument::
contentsChanged,
this
, &
amp;MainWindow::
documentWasModified);
#ifndef QT_NO_SESSIONMANAGER
QGuiApplication::
setFallbackSessionManagementEnabled(false
);
connect(qApp, &
amp;QGuiApplication::
commitDataRequest,
this
, &
amp;MainWindow::
commitData);
#endif
setCurrentFile(QString());
setUnifiedTitleAndToolBarOnMac(true
);
}
In the constructor, we start by creating a QPlainTextEdit widget as a child of the main window (the this object). Then we call QMainWindow::setCentralWidget() to tell that this is going to be the widget that occupies the central area of the main window, between the toolbars and the status bar.
Then we call createActions() and createStatusBar(), two private functions that set up the user interface. After that, we call readSettings() to restore the user's preferences.
We establish a signal-slot connection between the QPlainTextEdit's document object and our documentWasModified() slot. Whenever the user modifies the text in the QPlainTextEdit, we want to update the title bar to show that the file was modified.
At the end, we set the window title using the private setCurrentFile() function. We'll come back to this later.
void
MainWindow::
closeEvent(QCloseEvent *
event)
{
if
(maybeSave()) {
writeSettings();
event-&
gt;accept();
}
else
{
event-&
gt;ignore();
}
}
When the user attempts to close the window, we call the private function maybeSave() to give the user the possibility to save pending changes. The function returns true if the user wants the application to close; otherwise, it returns false. In the first case, we save the user's preferences to disk and accept the close event; in the second case, we ignore the close event, meaning that the application will stay up and running as if nothing happened.
void
MainWindow::
newFile()
{
if
(maybeSave()) {
textEdit-&
gt;clear();
setCurrentFile(QString());
}
}
The newFile() slot is invoked when the user selects File|New from the menu. We call maybeSave() to save any pending changes and if the user accepts to go on, we clear the QPlainTextEdit and call the private function setCurrentFile() to update the window title and clear the windowModified flag.
void
MainWindow::
open()
{
if
(maybeSave()) {
QString fileName =
QFileDialog::
getOpenFileName(this
);
if
(!
fileName.isEmpty())
loadFile(fileName);
}
}
The open() slot is invoked when the user clicks File|Open. We pop up a QFileDialog asking the user to choose a file. If the user chooses a file (i.e., fileName is not an empty string), we call the private function loadFile() to actually load the file.
bool
MainWindow::
save()
{
if
(curFile.isEmpty()) {
return
saveAs();
}
else
{
return
saveFile(curFile);
}
}
The save() slot is invoked when the user clicks File|Save. If the user hasn't provided a name for the file yet, we call saveAs(); otherwise, we call the private function saveFile() to actually save the file.
bool
MainWindow::
saveAs()
{
QFileDialog dialog(this
);
dialog.setWindowModality(Qt::
WindowModal);
dialog.setAcceptMode(QFileDialog::
AcceptSave);
if
(dialog.exec() !=
QDialog::
Accepted)
return
false
;
return
saveFile(dialog.selectedFiles().first());
}
In saveAs(), we start by popping up a QFileDialog asking the user to provide a name. If the user clicks Cancel, the returned file name is empty, and we do nothing.