Using QSettings in a global static class

2020-06-29 10:02发布

My task is to create a QSettings wrapper class (wrapping is mostly needed by QML) which I can reach everywhere from the model, too.

The obvious choice is a static global instance of this class. However the problem is with this approach is that it's destroyed after main, after QApplication is destroyed, thus giving me the following warning:

QApplication::qAppName: Please instantiate the QApplication object first

Here is a simplified, sample code showing a really simple wrapper, and the destruction phases:

#include <QCoreApplication>
#include <QDebug>
#include <QGlobalStatic>
#include <QSettings>
#include <QTimer>

class Settings: public QObject
{
public:
    ~Settings() { qDebug() << "~Settings"; }
    QSettings settings;
};

Q_GLOBAL_STATIC(Settings, globalSettings)

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){qDebug() << "~QCoreApplication aboutToQuit";});
    QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){globalSettings->settings.setValue("Hi", 2);});
    QObject::connect(&app, &QCoreApplication::destroyed, [](){qDebug() << "~QCoreApplication destroyed";});

    QTimer::singleShot(0, &app, SLOT(quit()));

    return app.exec();
}

It prints out the following:

~QCoreApplication aboutToQuit
~QCoreApplication destroyed
~Settings
QApplication::qAppName: Please instantiate the QApplication object first

My question is: providing, in my program QSettings is used after QCoreApplication::aboutToQuit is emitted but before QCoreApplication::destroyed, how can I avoid this warning?

标签: qt
1条回答
ゆ 、 Hurt°
2楼-- · 2020-06-29 10:08

Using QSettings

I've used QSettings in pretty much every project I've ever made. Here is the pattern that I tend to use it:

in main.cpp

#include <QSettings>

//...

// Then in main(), after QApplication is instantiated, but before any settings are accessed

QSettings::setDefaultFormat(QSettings::IniFormat);
QApplication::setOrganizationName("MyOrg");
QApplication::setApplicationName("MyApp"):

Then anytime you are about to access QSettings, you just do this:

QSettings s;

s.value(// reading a value
s.setValue(// writing a value

Everything gets saved in the User Scope in an INI text file. It will be located in Windows under C:/Users/<username>/AppData/Roaming/MyOrg/MyApp.ini.

This usage of QSettings (IMHO) is very clean, doesn't require global variables or static references and is very fast and efficient. And it is very readable.

Now to be able to have things load settings at the right times, and not get the errors you mentioned in your question, see the initial example in the links below:

http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application

http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html

It works much better in the timeline of a Qt application and works great. I tend to make a readSettings and writeSettings for most of my GUI classes and for a number of my backend classes. readSettings happens when the widget has its showEvent or constructor happen and the writeSettings happens in the closeEvent. Also if I have a dedicated settings dialog, I emit a signal to have any affected classes to readSettings, right after the settings dialog writes those specific settings.

If you use the QML port of QSettings, it also uses the Organization name and Application name and the default format of QSettings to pick its filename and location.

http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html

I believe the default functionality of that QML component is just to read the setting when the component is created, and to write the setting whenever QML changes it. So to change it from C++ and have it recognized by QML, you should probably use the standard QML/C++ methods out there such as:

http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html

And lastly if I am planning on installing defaults for a program that are decided for a build and I don't want to hardcode them, I hand write an INI file and have the installer place it in the system scope location: C:/ProgramData/MyOrg/MyApp.ini

And in the case that the settings of your program are more complex than what you want to store in an INI file, I would look into using QJson, and the SaveGame example.

Hope that helps.

查看更多
登录 后发表回答