Restoring Qt Application State and Geometry: Part 2

Read the first tutorial 

In the first part of this tutorial series, we went over the main.cpp file and a brief overview of what we wanted to accomplish in this application. Now we’ll go over the MainWindow class and save the state upon close of the window. First, we’ll look at the header file:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSettings>
#include <QByteArray>

namespace Ui
{
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    void closeEvent(QCloseEvent *);

private slots:
    void on_actionQuit_triggered();

private:
    Ui::MainWindow *ui;
    QSettings *appSettings;

    // Some functions we will use for restoring/writing the
    // necessary data
    void writeSettings();
    void readSettings();
    void restore();
};

#endif // MAINWINDOW_H

First we include the necessary Qt header files that we’ll need in the class implementation. We use aggregation by pointer to get access to our UIC generated class. For this example, my  UI form is simply a blank window with a File > Quit action–as apparent by the on_actionQuit_triggered() private slot that the Designer so kindly generated for me.

Window States Tutorial UI Form

The simple UI form I put together using Qt Creator’s integrated form designer.

For overriding the close event, we need to include the closeEvent method under protected, as the virtual method comes from QMainWindow–the base class in this particular case. Unless you need to modify the event directly, you can just leave it without a parameter other than the virtual QCloseEvent*.

In our private member section, we declare a pointer to a QSettings instance. I used a pointer as with most members such as these, so that we can control its lifespan and reap the benefits of using a pointer, while keeping our memory in check with the QObject parenting tree. You’ll see this later when we pass the QObject of the class instance into the constructor of the appSettings pointer.

Now that the header file is out of the way, lets take a look at the implementation:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow)
{
    // Pay attention to the information you enter here.
    appSettings = new QSettings("Contingency Bloggers Corporation",
                                "Save State", this);

    // Restore the UI
    readSettings();

    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// Inject code into the Close Event
void MainWindow::closeEvent(QCloseEvent *)
{
    // This will be called whenever this window is closed.
    writeSettings();
}

void MainWindow::writeSettings()
{
    // Write the values to disk in categories.
    appSettings->setValue("state/mainWindowState", saveState());
    appSettings->setValue("geometry/mainWindowGeometry",
                                           saveGeometry());
}

void MainWindow::restore()
{
    // We have to call toByteArray because the
    // values are stored as QVariant's
    QByteArray stateData = appSettings->value
            ("state/mainWindowState").toByteArray();

    QByteArray geometryData = appSettings->value
            ("geometry/mainWindowGeometry").toByteArray();

    restoreState(stateData);
    restoreGeometry(geometryData);
}

void MainWindow::readSettings()
{
    // This is a call to our restore() method.
    // We use this so we can keep the purposes of the
    // different methods clear if we were to expand.
    restore();
}

void MainWindow::on_actionQuit_triggered()
{
    // Make sure we write the settings. We could close the window,
    // but I prefer using qApp->quit() to clean up anything else.
    writeSettings();

    qApp->quit();
}

As you can see, it’s a bit long, but certainly manageable if we break it up into the individual parts. Firstly, you have the MainWindow constructor. Here we initialize the appSettings pointer, and call readSettings() to set GUI elements back to their old state. Then we just call the Ui method setupUi, which, well, sets up our UI. After that we just have the destructor where we can put memory deallocation in. Currently the only pointer that needs to be deleted is the ui member.

In the implementation of the virtual closeEvent, all we do is call our writeSettings() function. In the writeSettings() function, we write the values into keys stored under their respective categories. We could use a group, but it’s just two values, and I am putting them under their own keys anyway (“editor” and “geometry”).

Probably the most “interesting” function is the restore() function. First, we recall the values and set some Byte Arrays equal to the values. We can use the QVariant function .toByteArray() to convert the value to a specific type, instead of casting which could lead to some problems depending on how the QVariant stores the data. We then call the restore functions with the respective byte arrays. In readSettings(), we call this method. As mentioned in the comments, I separated these so you could keep regular settings separate more easily.

Lastly, in the quit action’s triggered slot, we call writeSettings() to ensure that the settings are saved. You can call this function basically anywhere that you want the geometry and state to be saved, like a quit button.

In the next part, we’ll take a look at the different usages of the methods presented in this tutorial.

Edit

A commenter has pointed out that I’m calling readSettings() before creating the UI in these examples. The constructor should make the call to readSettings() after the UI is created.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow)
{
    // Pay attention to the information you enter here.
    appSettings = new QSettings("Contingency Bloggers Corporation",
                                "Save State", this);

    ui->setupUi(this);

    // Restore the UI
    readSettings();
}
Advertisements

2 thoughts on “Restoring Qt Application State and Geometry: Part 2

  1. rich

    In your code example you call readSettings() before creating the gui [ui->setupUi(this);]
    This can never work properly, when you are using toolbar, dockwidget etc.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s