PDA

View Full Version : How to best access configuration and options data with Qt



Momergil
5th May 2013, 16:35
Hello!

Normally somewhat big applications have a specific dialog window for Options and/or Configuration, accessible by the menu bar, where the user may configure the software as it pleases him. Sometimes part of the configuration data are values that are used elsewhere in the software (i.e. during the usage of the software it will ask "have the user checked that combobox with that option or not? If yes, I'll do something; if not, I'll do something else").

The problem is that since the configuration is done in a dialog "appart" from the software itself, I'm dealing with the problem of how exactly make the rest of the application to know the settings that were put by the user.

I imagined using QSettings to do this, since thoose configuration should be avaliable in the next opening. So when the settings dialog window is closed, the close method would save the values with QSettings and each time the software wants to know the definition, it would open a new QSettings and find the configuration that was set. The problem with this method is that it doesn't seem right to use QSettings this way; I learned that QSettings was created to save configuration between usages of the software (so when it opens it will have the previous configuration), not to access configuration during the software usages.

But if QSettings is not appropriate for that, which other way should I use? I thought about saving all data into a large Settings struct, but it seems that would have a problem if a needed that configuration data in child widgets - for I would have to send all the struct to the child widget or between threads in order for it to access that data, what seems to much memory comsumption.

So my question would be: which is the best way to handle this configuration data in Qt? I remember also reading in the QSettings manual:



If all you need is a non-persistent memory-based structure, consider using QMap<QString, QVariant> instead.


but that would give the same problems that I have with the struct method.


Thanks,

Momergil

Lesiok
5th May 2013, 18:50
I learned that QSettings was created to save configuration between usages of the software (so when it opens it will have the previous configuration), not to access configuration during the software usages.
Where did You learned this ? From the QSettings doc : QSettings is reentrant. This means that you can use distinct QSettings object in different threads simultaneously. This guarantee stands even when the QSettings objects refer to the same files on disk (or to the same entries in the system registry). If a setting is modified through one QSettings object, the change will immediately be visible in any other QSettings objects that operate on the same location and that live in the same process.

QSettings can safely be used from different processes (which can be different instances of your application running at the same time or different applications altogether) to read and write to the same system locations. It uses advisory file locking and a smart merging algorithm to ensure data integrity. Note that sync() imports changes made by other processes (in addition to writing the changes from this QSettings).

d_stranz
5th May 2013, 19:34
I think the problem might be that you are not configuring QSettings correctly. See this part of the documentation:


If you use QSettings from many places in your application, you might want to specify the organization name and the application name using QCoreApplication::setOrganizationName() and QCoreApplication::setApplicationName(), and then use the default QSettings constructor:



QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");
// ...
QSettings settings;



Do this at app startup time; in main() is an OK place, or in your MainWindow() constructor. Just be sure to do it before using QSettings for the first time. If you are running on Windows, QSettings will use the Registry by default. If you don't want that, then call
QSettings::setDefaultFormat( QSettings::IniFormat ); (again, before using any QSettings in your app).

And as Lesiok said, once this is done, you can use QSettings from anywhere in your app, even multiple places at the same time, and a change made in one place will be visible in another (the next time you ask for the value that was changed).

This is convenient, but using a QMap< QString, QVariant > might be faster. Simply interface this to restore the map at startup time from QSettings, then save it back when the app closes. The major downside of this is that if you app crashes (yours might; none of mine ever do ;)) you lose any changes that were not saved.

And if you want to use the QMap<> approach, then the way to do it is to make it a "global variable". I do this by deriving a new class from QApplication and adding a call to retrieve it:


typedef QMap< QString, QVariant > MySettings;

class MyApp : public QApplication
{
// ...
public:
const MySettings & settings() const { return settings; } // retrieve for read-only use
MySettings & settings() { return settings; } // retrieve for read / write access

private:
MySettings settings;
};

MyApplication::MyApplication( int argc, char * * argv ) : QApplication( argc, argv )
{
QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");

QSettings::setDefaultFormat( QSettings::IniFormat );

settings.restore(); // you'll write this to retrieve from QSettings
}

~MyApplication::MyApplication()
{
settings.save(); // you'll write this too.
}

And if you want to, you could make MySettings a QObject-based class that would emit a signal every time a value was changed so that the app could persist the data to QSettings at that time. You would be protected then against crashes.

Momergil
6th May 2013, 14:14
Well people, I think you got my question wrong ^^


Where did You learned this ?



Users normally expect an application to remember its settings (window sizes and positions, options, etc.) across sessions. This information is often stored in the system registry on Windows, and in XML preferences files on Mac OS X. On Unix systems, in the absence of a standard, many applications (including the KDE applications) use INI text files.

QSettings is an abstraction around these technologies, enabling you to save and restore application settings in a portable manner. It also supports custom storage formats.


In other words, I know I can call QSettings lots of time and from different places in my software, I just don't think that's the way it was build to be used.



This is convenient, but using a QMap< QString, QVariant > might be faster. Simply interface this to restore the map at startup time from QSettings, then save it back when the app closes. The major downside of this is that if you app crashes (yours might; none of mine ever do ) you lose any changes that were not saved.


As I sad, I noticed this possibility and one of the reasons why I disliked it a little was because of this crash possibility (yeah, surely your apps don't crash ^^). The other was about passing the data between different objects (dialog windows); it might be too much memory comsumption.

---
So the methods I can see by now are:

* When the software is opened, it loads from QSettings the previous configuration. Each time thoose data are required, a new QSettings object is created to retrieve that data. In the Settings window, all configuration is stored in QSettings. At the end of the software, nothing is done.
* When the software is opened, a QSettings object loads all settings to a QMap<QString,QVariant>. When data is required, it loads from the QMap. When the user wants to chance the configuration. it opens the Settings that load the configuration either from a QSettings. When the settings process is done, the new configuration is saved to QSettings and to the QMap. When the software is finished, the QMap is deleted.
* When the software is opened, a QSettings object loads all settings to a QMap<QString,QVariant>. When data is required, it loads from the QMap. When the user wants to chance the configuration. it opens the Settings that load the configuration from the QMap. The configuration process is finished and the new configuration is stored to the QMap. When the software is finished, a QSettings object saves the QMap. (my last preferible option).

Any other ideas and which of thoose would be the best?

Lesiok
6th May 2013, 16:16
Why sure you want to complicate such a simple thing? Use QSettings and so.

anda_skoa
6th May 2013, 16:18
You can also keep the QSettings object around and use it instead of the map.

Cheers,
_

Momergil
6th May 2013, 16:53
Why sure you want to complicate such a simple thing? Use QSettings and so.

Yep, I guess I'ld end up doing so ^^ But as I put in the question, I'ld like to use the best way, if exists, to access such configuration data. You know, trying always to make the difference between a noob programmer and someone with experience and will enough to make things a little more decent =]


You can also keep the QSettings object around and use it instead of the map.

You mean, a global QSettings object? Well, I don't see much advantage with that except when compared to creating local QSettings each time I need the parameters - less cpu consumption.

wysota
6th May 2013, 17:03
Yep, I guess I'ld end up doing so ^^ But as I put in the question, I'ld like to use the best way, if exists, to access such configuration data.
The best way (as already advised by anda_skoa) is to use QSettings and to make the object accessible to all parts of your software once the settings are loaded (either by passing a pointer around or by making the settings object available via a singleton).

Momergil
6th May 2013, 20:11
The best way (as already advised by anda_skoa) is to use QSettings and to make the object accessible to all parts of your software once the settings are loaded (either by passing a pointer around or by making the settings object available via a singleton).

Hmm, ok. I only don't know about the "once the settings are loaded": If I'm not going to use QMaps, struct or similar, than the settings only are loaded this way when the users opens the Settings window. Appart from that, the software will load each setting by time as the need appears.

So than:

I create a global QSettings pointer and each time a config value is needed, the pointer access the value directly. If I create a child window, I pass the pointer as a parameter in the constructor. In case of different threads, no problem about creating a pointer for each one since QSettings is thread safe and they will only read the data anyway.

Thanks!

Momergil

wysota
6th May 2013, 20:51
Hmm, ok. I only don't know about the "once the settings are loaded"


QSettings *settings = 0;
vs

QSettings *settings = new QSettings("mysettingsfile.ini", ...);