PDA

View Full Version : QSettings - how to use?



almalino
3rd January 2011, 18:33
Dear QT experts,

There are at least 2 ways to use QSettings in the application:

Way 1:
1. Read settings into memory (object variables etc) at the application start
2. Use settings from the memory
3. Write them to disk when application closes

class SampleClass
{
QString login;
SampleClass {login = settings->value("login","").toString();};
}

Way 2:
Do not allocate variables for settings but read them whenever they are needed. And update them in QSettings whenever user changed them.

class SampleClass
{
QString login(void) { return settings->value("login","").toString(); };
}

There are advantages and disadvantages for both cases. What I like about second way is that there is no data duplication - you know that up to date information is only in one place - QSettings. But it, apparently , slower and settings reading is spread accross application code.

I would be glad to hear recommendation how to use QSettings class in a most efficient way.

SixDegrees
3rd January 2011, 18:38
Speed isn't really much of an issue for most cases; settings tend to be small, and in many cases the results of the first read will hang around in the I/O buffer anyway, making subsequent reads that much faster. However, if you're using QSettings as an adjunct to a general purpose preferences-style framework, you'll want an in-memory copy in most cases so the user can modify settings in a one-shot manner without overwriting the file-based set unless explicitly requesting such an overwrite. Here, your program will always read from the in-memory copy, which gets initialized during startup and might be modified at runtime through an options panel having both "Save" and "Use" (and "Cancel") choices.

almalino
3rd January 2011, 19:21
SixDegrees, thank you for your opinion! I got the idea.

javimoya
3rd January 2011, 19:51
my 2 cents:
I try to avoid callings to qsettings...

in my QMainWindow (or in other dialog)
i read settings related to that dialog at the beggining into variables...
and in the close event i write to qsettings. (or in accept() )

if my main window open a dialog who need read a setting of main window... i'll pass as a parameter.

only I read settings directly when the setting is not specific for any dialog..

Atomic_Sheep
20th September 2017, 14:05
I made my settings code with a bunch of variables. But now that I think about it, it probably makes more sense to access settings in the ini/reg file directly since you're reducing the amount of memory needed for the stack.

Comments?

d_stranz
20th September 2017, 18:59
Comments?

1 - Caching the settings into variables on startup and writing changes back out at exit will make your code run faster, at the expense of some small amount of extra memory. If you create MainWindow on the stack in main(), then these variables will also live on the stack.

2 - Accessing QSettings as needed from within your code is slower, but you don't need to find ways of passing all those variable values around. I wrote a little wrapper class that has static readValue() and writeValue() methods. Inside the static methods, it creates a QSettings instance on the stack that is initialized with the organization and application name (obtained from qApp()), sets it to User mode and Ini format and reads or writes the values through that. The qApp settings are set in main() at program startup. So in one line I can write something like this with all of the QSettings construction stuff hidden away:



MySettings::writeValue( "Foo/Bar", "Baz" );


3 - Caching the settings means that if your program crashes, you lose any changes because you never get to the MainWindow's writeSettings() call.

4 - Reading and writing settings on demand means that changes are immediately committed. QSettings is thread- and process-safe.

In most of my applications, I use method (2) because the overhead is negligible and I never do it from within process-critical code. It is also way more convenient with the wrapper class. You could even get fancy and provide multiple readValue() methods that do the QVariant conversion for you:



// static
int MySettings::readValue( const QString & key, int defaultVal )
{
QSettings settings( QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName );
QVariant var = settings.value( key, QVariant( defaultVal ) );

bool bOk;
int testVal = var.toInt( &bOk );
if ( bOk )
return testVal;
else
return defaultVal;
}

Atomic_Sheep
21st September 2017, 04:09
Thanks for that, very helpful, I'm leaning in the direction of on demand access to ini/reg files as well. I'm already creating LoadSettings() and SaveSettings() functions in 3 different classes and the associated member variables and I'm only starting out.

Ah you're one step ahead of me. After starting to write a class for managing settings as you suggested I immediately ran into the problem of dealing with different data types. Reading about QVariant, seems like it's basically a placeholder for different datatypes?

Atomic_Sheep
21st September 2017, 13:02
EDIT: Something to do with unions. No idea what they are just learned about them, need to do some reading.

d_stranz
22nd September 2017, 00:12
Something to do with unions.

Don't worry about how QVariant is implemented internally, you'll just wind up leading yourself astray and your sheep dog will have to round you up with a lot of barking and nipping at heels. Yes, it *acts* like a union in that you can store as one type and retrieve as another, but just know (and appreciate) that you can store any fundamental type (int, float, double, etc.), nearly any Qt type (QString, etc.), and any custom type (with the right wrapper) in one and retrieve it afterwards exactly as it was stored. In this way it goes well beyond the C++ definition of union. It is more like a general purpose container that can hold one thing of any type.

The main use for QVariant is to allow you to write one method (like QSettings::setValue()) that allows you to store a value of any type (wrapped in a QVariant) as opposed to writing a zillion different setValue() methods, one for int, one for float, one for long, etc. as you usually have to do for the ">>" and "<<" operators.

Atomic_Sheep
22nd September 2017, 02:41
Oh I wasn't looking into the implementation of QVariant, I was just reading about unions since I had not heard of them before, now I know what they are and as you described, what QVariant is. The only question that I have about your code is why the return type is int if you're creating a function that can store any value? And why are you only accepting int as a parameter? Seems like QVariant is not necessary for your snippet? Could have gotten away with just an int?

I'm thinking code like this might be a bit more universal?


QVariant MySettings::readValue(const QString & key)
{
QSettings settings( QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName );
QVariant var = settings.value(key);

return var;
}


void MySettings::writeValue(const QString & key, QVariant(val))
{
QSettings settings.setValue(key, val);
}

PS I haven't compiled it, so might be synatx errors, but for reading, you only need one variable and that's the name of the settings.

For writing, you simply put in whatever variable you want and reading will output the variable in the same format.

d_stranz
22nd September 2017, 05:17
And why are you only accepting int as a parameter?

Well, in actual fact I am doing it that way:



// static
QVariant QSettingsHelper::readValue( const QString & group, const QString & key, const QVariant & defValue )
{
QSettings settings( QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName() );

settings.beginGroup( group );
QVariant value = settings.value( key, defValue );
settings.endGroup();

return value;
}

// static
void QSettingsHelper::writeValue( const QString & group, const QString & key, const QVariant & value )
{
QSettings settings( QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName() );

settings.beginGroup( group );
settings.setValue( key, value );
settings.endGroup();
}