1

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?

szotsaki
  • 684
  • 7
  • 16
  • 1
    Why don't you use the settings port that already exists for QML? http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html – phyatt Jun 02 '15 at 23:43
  • 3
    *The obvious choice is a static global instance of this class. However the problem is...* Then the obvious choice is the wrong way to go. – UmNyobe Jun 03 '15 at 00:28
  • 1
    Static globals are a world of pain (undefined and uncontrollable initialization and destruction order). I'd create the instance on the stack, e.g. in main() and export it to qml from there, or use the class phyatt suggests – Frank Osterfeld Jun 03 '15 at 05:41
  • @phyatt: As I saw, the QML version is extremely limited, and that's why it's not a coincidence it's still a Labs product. For example I also need to reach the very same Settings object from the model (aka. C++ code in order to use the same path everywhere) and store everything in `.ini` files even under Windows. That is what I cannot do with the QML version. – szotsaki Jun 03 '15 at 12:10

1 Answers1

7

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.

phyatt
  • 18,472
  • 5
  • 61
  • 80
  • I badly overlooked the part that some functions of QSettings are static and set its state globally. Basically I created the wrapper class because of this very reason. Thank you for the detailed answer. – szotsaki Jun 09 '15 at 20:30
  • np. Glad I could help. I think I wasted several hours looking at alternatives early on in my Qt learning because this information wasn't obvious. – phyatt Jun 09 '15 at 20:46