- Qt 5 Blueprints
- Symeon Huang
- 1462字
- 2021-07-23 19:48:18
Saving and restoring settings
Although the clock can be moved, it won't restore its last position after restarting. In addition to this, we can give users some choices to adjust the clock's appearance, such as the font color. To make it work, we need the QSettings
class, which provides platform-independent persistent settings. It needs a company or organization name and the name of an application. A typical QSettings
object can be constructed by using this line:
QSettings settings("Qt5 Blueprints", "Fancy Clock");
Here, Qt5 Blueprints
is the organization's name and Fancy Clock
is the application's name.
The settings are stored in the system registry on Windows, while they are stored in the XML preferences files on Mac OS X and the INI text files on the other Unix operating systems, such as Linux. However, we do not usually need to be concerned with this, since QSettings
provides high-level interfaces to manipulate the settings.
If we're going to read and/or write settings in multiple places, we'd better set the organization and application in QCoreApplication
, which is inherited by QApplication
. The main.cpp
file's content is shown as follows:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); a.setOrganizationName(QString("Qt5 Blueprints")); a.setApplicationName(QString("Fancy Clock")); QWidget wid; MainWindow w(&wid); w.show(); return a.exec(); }
This enables us to use the default QSettings
constructor to access the same settings. In order to save the geometry and state of MainWindow
, we need to reimplement closeEvent
. First, we need to declare closeEvent
to be a protected member function, as follows:
void closeEvent(QCloseEvent *);
Then, let's define the closeEvent
function in mainwindow.cpp
, as follows:
void MainWindow::closeEvent(QCloseEvent *e) { QSettings sts; sts.setValue("MainGeometry", saveGeometry()); sts.setValue("MainState", saveState()); e->accept(); }
Remember to add #include <QSettings>
in order to include the QSettings
header files.
Thanks to setOrganizationName
and setApplicationName
, we don't need to pass any arguments to the QSettings
constructor now. Instead, we call a setValue
function to save the settings. The saveGeometry()
and saveState()
functions return the MainWindow
geometry and state respectively as the QByteArray
objects.
The next step is to read these settings and restore the geometry and state. This can be done inside the constructor of MainWindow
. You just need to add two statements to it:
QSettings sts; restoreGeometry(sts.value("MainGeometry").toByteArray()); restoreState(sts.value("MainState").toByteArray());
Here, toByteArray()
can translate the stored value to a QByteArray
object. How do we test to see if this works? To do this, perform the following steps:
- Rebuild this application.
- Run it.
- Move its position.
- Close it.
- Run it again.
You'll see that the clock will appear at exactly the same position as it was before it closed. Now that you're pretty much familiar with widgets, layouts, settings, signals, and slots, it's time to cook a preference dialog by performing the following steps:
- Right-click on the
Fancy_Clock
project in the Projects panel. - Select Add New….
- Select Qt in the Files and Classes panel.
- Click on Qt Designer Form Class in the middle panel.
- Select Dialog with Buttons Bottom.
- Fill in
Preference
under Class name. - Click on Next, and then select Finish.
Qt Creator will redirect you to the Design mode. First, let's change windowTitle
to Preference, and then do some UI work. Perform the following steps to do this:
- Drag Label to
QDialog
and change itsobjectName
property tocolourLabel
. Next, change its text toColour
. - Add QComboBox and change its
objectName
property tocolourBox
. - Add the
Black
,White
,Green
, andRed
items tocolourBox
. - Change the layout of
Preference
to Lay Out in a Form Lay Out.
Close this UI file. Go back to editing the preference.h
add a private onAccepted
slot. The following code shows the content of this file:
#ifndef PREFERENCE_H #define PREFERENCE_H #include <QDialog> namespace Ui { class Preference; } class Preference : public QDialog { Q_OBJECT public: explicit Preference(QWidget *parent = 0); ~Preference(); private: Ui::Preference *ui; private slots: void onAccepted(); }; #endif // PREFERENCE_H
As usual, we define this slot in the source file. Besides, we have to set up some initializations in the constructor of Preference
. Thus, preference.cpp
becomes similar to the following code:
#include <QSettings> #include "preference.h" #include "ui_preference.h" Preference::Preference(QWidget *parent) : QDialog(parent), ui(new Ui::Preference) { ui->setupUi(this); QSettings sts; ui->colourBox->setCurrentIndex(sts.value("Colour").toInt()); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &Preference::onAccepted); } Preference::~Preference() { delete ui; } void Preference::onAccepted() { QSettings sts; sts.setValue("Colour", ui->colourBox->currentIndex()); }
Similarly, we load the settings and change the current item of colourBox
. Then, it's the signal and slot coupling that follow. Note that Qt Creator has automatically generated the accept and reject connections between buttonBox
and Preference
for us. The accepted
signal of buttonBox
is emitted when the OK button is clicked. Likewise, the rejected
signal is emitted if the user clicks on Cancel. You may want to check Signals & Slots Editor in the Design mode to see which connections are defined there. This is shown in the following screenshot:

As for the definition of the onAccepted
slot, it saves currentIndex
of colourBox
to the settings so that we can read this setting elsewhere.
Now, what we're going to do next is add an entry for Preference
in the pop-up menu and change the color of lcdNumber
according to the Colour
setting value. Therefore, you should define a private slot and a private member function in mainwindow.h
first.
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; QPoint m_mousePos; void setColour(); private slots: void updateTime(); void showContextMenu(const QPoint &pos); void showPreference(); protected: void mouseReleaseEvent(QMouseEvent *); void mousePressEvent(QMouseEvent *); void mouseMoveEvent(QMouseEvent *); void closeEvent(QCloseEvent *); }; #endif // MAINWINDOW_H
The setColour
function is used to change the color of lcdNumber
, while the showPreference
slot will execute a Preference
object. The definitions of these two members are in the mainwindow.cpp
file, which is displayed in the following manner:
#include <QTimer> #include <QTime> #include <QMouseEvent> #include <QMenu> #include <QAction> #include <QSettings> #include "mainwindow.h" #include "preference.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | windowFlags()); QSettings sts; restoreGeometry(sts.value("MainGeometry").toByteArray()); restoreState(sts.value("MainState").toByteArray()); setColour(); connect(this, &MainWindow::customContextMenuRequested, this, &MainWindow::showContextMenu); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::updateTime); timer->start(1000); updateTime(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::updateTime() { QTime currentTime = QTime::currentTime(); QString currentTimeText = currentTime.toString("hh:mm"); if (currentTime.second() % 2 == 0) { currentTimeText[2] = ' '; } ui->lcdNumber->display(currentTimeText); } void MainWindow::showContextMenu(const QPoint &pos) { QMenu contextMenu; contextMenu.addAction(QString("Preference"), this, SLOT(showPreference())); contextMenu.addAction(QString("Exit"), this, SLOT(close())); contextMenu.exec(mapToGlobal(pos)); } void MainWindow::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton) { emit customContextMenuRequested(e->pos()); } else { QMainWindow::mouseReleaseEvent(e); } } void MainWindow::mousePressEvent(QMouseEvent *e) { m_mousePos = e->pos(); } void MainWindow::mouseMoveEvent(QMouseEvent *e) { this->move(e->globalPos() - m_mousePos); } void MainWindow::closeEvent(QCloseEvent *e) { QSettings sts; sts.setValue("MainGeometry", saveGeometry()); sts.setValue("MainState", saveState()); e->accept(); } void MainWindow::setColour() { QSettings sts; int i = sts.value("Colour").toInt(); QPalette c; switch (i) { case 0://black c.setColor(QPalette::Foreground, Qt::black); break; case 1://white c.setColor(QPalette::Foreground, Qt::white); break; case 2://green c.setColor(QPalette::Foreground, Qt::green); break; case 3://red c.setColor(QPalette::Foreground, Qt::red); break; } ui->lcdNumber->setPalette(c); this->update(); } void MainWindow::showPreference() { Preference *pre = new Preference(this); pre->exec(); setColour(); }
We call setColour
in the constructor in order to set the color of lcdNumber
correctly. Inside setColour
, we first read the Colour
value from the settings, and then use a switch
statement to get the correct QPalette
class before calling setPalette
to change the color of lcdNumber
. Since Qt doesn't provide a direct way to change the foreground color of the QLCDNumber
objects, we need to use this tedious method to achieve this. At the end of this member function, we call update()
to update the MainWindow
user interface.
In the relevant showPreference
slot, we create a new Preference
object, which is the child of MainWindow
, and then call exec()
to execute and show it. Lastly, we call setColour()
to change the color of lcdNumber
. As Preference
is modal and exec()
has its own event loop, it will block the application until pre
is finished. After pre
finishes executing, either by accepted
or rejected
, setColour
will be called next. Of course, you can use the signal-slot way to implement it, but we have to apply some modifications to the previous code. Firstly, delete the accepted-accept
signal-slot couple in preference.ui
in the Design mode. Then, add accept()
to the end of onAccepted
in preference.cpp
.
void Preference::onAccepted() { QSettings sts; sts.setValue("Colour", ui->colourBox->currentIndex()); this->accept(); }
Now, showPreference
in mainwindow.cpp
can be rewritten as follows:
void MainWindow::showPreference() { Preference *pre = new Preference(this); connect(pre, &Preference::accepted, this, &MainWindow::setColour); pre->exec(); }
No matter which way you prefer, the clock should have a Preference dialog now. Run it, select Preference from the pop-up menu, and change the color to whatever you please. You should expect a result similar to what is shown in the following screenshot:

- Flutter開發實戰詳解
- 微信公眾平臺與小程序開發:從零搭建整套系統
- Getting started with Google Guava
- Ray分布式機器學習:利用Ray進行大模型的數據處理、訓練、推理和部署
- Visual Basic程序設計與應用實踐教程
- HTML5+CSS3+JavaScript Web開發案例教程(在線實訓版)
- ASP.NET程序開發范例寶典
- Unity&VR游戲美術設計實戰
- Apache Camel Developer's Cookbook
- Julia High Performance(Second Edition)
- Java EE架構設計與開發實踐
- Web前端開發技術實踐指導教程
- C/C++程序設計教程
- 樹莓派開發從零開始學:超好玩的智能小硬件制作書
- Build Your Own PaaS with Docker