官术网_书友最值得收藏!

  • 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:

  1. Rebuild this application.
  2. Run it.
  3. Move its position.
  4. Close it.
  5. 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:

  1. Right-click on the Fancy_Clock project in the Projects panel.
  2. Select Add New….
  3. Select Qt in the Files and Classes panel.
  4. Click on Qt Designer Form Class in the middle panel.
  5. Select Dialog with Buttons Bottom.
  6. Fill in Preference under Class name.
  7. 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:

  1. Drag Label to QDialog and change its objectName property to colourLabel. Next, change its text to Colour.
  2. Add QComboBox and change its objectName property to colourBox.
  3. Add the Black, White, Green, and Red items to colourBox.
  4. 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:

Saving and restoring settings

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.

Note

Don't forget to add the Preference action to contextMenu inside showContextMenu. Otherwise, there will be no way to open the dialog.

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();
}

Tip

The connect statement shouldn't be placed after exec(), as it will cause the binding to fail.

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:

Saving and restoring settings
主站蜘蛛池模板: 通江县| 自贡市| 濮阳县| 云浮市| 甘泉县| 鹿泉市| 包头市| 铁岭县| 辽阳市| 泰宁县| 邓州市| 吉隆县| 疏勒县| 牡丹江市| 梁山县| 文成县| 东乌珠穆沁旗| 都江堰市| 兴安县| 鹤峰县| 兴文县| 平邑县| 高邑县| 理塘县| 平潭县| 安康市| 临颍县| 永昌县| 天气| 营山县| 余姚市| 西乡县| 明光市| 新疆| 满城县| 将乐县| 彭水| 图们市| 长宁县| 灵丘县| 信丰县|