10.3.  QSettings: Saving and Restoring Application State

[ fromfile: qsettings.xml id: qsettings ]

Most desktop applications have a way for users to configure the settings. The settings/preferences/options need to be persistent. The mechanism for that is included with QSettings.

A QSettings must be initialized with an application name, organization name, and organization domain before it is used for the first time. This prevents settings from one application from accidentally overwriting those from another. If, however, the QApplication itself is set with this information, as shown in Example 10.7, the QSettings default constructor uses it.

Example 10.7. src/widgets/mainwindow/mainwindow-main.cpp

#include "mymainwindow.h"
#include <QApplication>

int main( int argc, char ** argv ) {
    QApplication app( argc, argv );
    app.setOrganizationName("objectlearning");
    app.setOrganizationDomain("objectlearning.net");
    app.setApplicationName("mainwindow-test");
    MyMainWindow mw;
    mw.show();
    return app.exec();
}

Now that the QApplication is initialized, you can create QSettings instances using the default constructor.

As you develop a new QMainWindow application, the first persistent settings you may want to save are probably the window size and position. You may also want to save the names of the most recent documents that were opened by the application.

QSettings manages persistent maps of key/value pairs. It is a QObject, and uses something similar to QObject's property interface, setValue() and value(), to set and get its values. It can be used to store any data that needs to be remembered across multiple executions.

The QSettings needs an organization name and an application name but when you use the default constructor, QSettings gets those values from the QApplication. Each combination of names defines a unique persistent map that does not clash with settings from other-named Qt applications.

The actual mechanism for the persistent storage of QSettings data is implementation-dependent and quite flexible. Some possibilities for its storage include the Win32 registry (in Windows) and your $HOME/.settings directory (in Linux). For more detailed information see the Qt QSettings API documentation.

QMainWindow::saveState() returns a QByteArray that contains information about the main window's toolbars and dockwidgets. To do this it makes use of the objectName property for each of those subwidgets, thus making it important that each name be unique. saveState() has an optional int versionNumber parameter. The QSettings object stores that QByteArray with the key string "state".

QMainWindow::restoreState() takes a QByteArray, presumably created by saveState(), and uses the information that it holds to put the toolbars and dockwidgets into a prior arrangement. It too has an optional versionNumber parameter. Read the settings after constructing the object, as shown in Example 10.8.

Example 10.8. src/widgets/mainwindow/mymainwindow.cpp

[ . . . . ]

void MyMainWindow::readSettings() {
    QSettings settings;
    QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
    QSize size = settings.value("size", QSize(400, 400)).toSize();
    QByteArray state = settings.value("state", QByteArray())
                                                   .toByteArray();
    restoreState(state);
    resize(size);
    move(pos);
}

You should write settings after the user wants to quit but before the window is closed. The appropriate place for that is in an event handler, so that you can handle the event before the widget itself does. Example 10.9 shows an event handler for closeEvent() that saves this information to QSettings.

Example 10.9. src/widgets/mainwindow/mymainwindow.cpp

[ . . . . ]

void MyMainWindow::closeEvent(QCloseEvent* event) {
    if (maybeSave()) {
        writeSettings();
        event->accept();
    } else {
        event->ignore();
    }
}


void MyMainWindow::writeSettings() {
    /* Save postion/size of main window */
    QSettings settings;
    settings.setValue("pos", pos());
    settings.setValue("size", size());
    settings.setValue("state", saveState());
}