01-17-2013 03:45 AM - edited 01-17-2013 03:48 AM
I have run into a problem when storing bool values in QSettings. It looks like a bug to me, but maybe I am missing something?
I have this short piece of code:
QSettings* settings = new QSettings();
qDebug() << "test setting is of type " << settings->value("test", false).typeName();
settings->setValue("test", true);
qDebug() << "now test setting is of type " << settings->value("test", false).typeName();
On the first run it will print
test setting is of type bool
now test setting is of type bool
After that it will print
test setting is of type QString
now test setting is of type bool
On the first run there is no stored setting, so it takes the passed default value, but after the setting has been set once, it seems like it is stored as a QString and loaded as a QString on the next app run.
This is messing a lot of things up especially when passing the QVariant to QML.
My ugly workaround right now is to do a
settings->setValue('test', settings->value('test', false).toBool())
on launch for every bool setting that I have.
Anyone else seeing this?
Solved! Go to Solution.
01-17-2013 09:16 AM - edited 01-17-2013 09:18 AM
This is expected behavior although not very intuitive. ![]()
QSettings doesn't store type information in ini files. Always request the specific type when accessing QSetting using .toBool(), toInt(), toString() etc.
I think it's more correct than using the workaround above as there's no guarantee on how QSettings stores the data internally.
01-17-2013 12:59 PM
Thanks Zmey. I wasn't aware that the settings are being stored in .ini files, but in this case it makes sense. I probably was secretly hoping QSettings are serialized like PersistentStore on BB7 ;-)
I am exposing the QSettings object as a context property to QML. In QML I do not have a way to request a specific type by using toBool etc. Or is there a way?
01-17-2013 01:10 PM - edited 01-17-2013 01:14 PM
It depends on OS. It uses text files on Unix, registry on Windows, but can be switched to ini files and so on.
For QML I'd create a wrapper class with getters/setters:
Q_INVOKABLE bool soundEnabled();
Q_INVOKABLE void setSoundEnabled(bool enabled);
etc
Instantiate QSettings as a member variable. Call sync() in setters to write changes to disk:
int Settings::numericPrecision()
{
QVariant v = settings_.value(numericPrecisionKey);
if (v.isNull())
return 8; // default value
return v.toInt();
}
void Settings::setNumericPrecision(int precision)
{
settings_.setValue(numericPrecisionKey, QVariant(precision));
settings_.sync();
emit numericFormatChanged();
}
Also it might be a good idea to make Settings class a singleton so every page which uses these settings can subscribe to changes.
01-17-2013 02:07 PM
Thanks again Zmey.
I was trying to avoid using accessor methods for every single setting. What I have done now is to use a subclassed version of QSettings that introduces a boolValue, intValue etc. that I can use from QML.
class Settings : public QSettings {
Q_OBJECT
public:
Settings(QObject *parent = 0);
virtual ~Settings();
Q_INVOKABLE
void setValue(const QString &key, const QVariant &value);
Q_INVOKABLE
void setValueIfNotSet(const QString &key, const QVariant &value);
Q_INVOKABLE
QVariant value(const QString &key, const QVariant &defaultValue);
Q_INVOKABLE
bool boolValue(const QString &key, const bool defaultValue);
Q_INVOKABLE
void initToDefaults();
signals:
void settingChanged(const QString& key);
};
Settings::Settings(QObject* parent) :
QSettings(parent) {
}
Settings::~Settings() {
}
QVariant Settings::value(const QString &key, const QVariant &defaultValue = QVariant()) {
return QSettings::value(key, defaultValue);
}
bool Settings::boolValue(const QString &key, bool defaultValue) {
return QSettings::value(key, defaultValue).toBool();
}
void Settings::setValue(const QString &key, const QVariant &value) {
// change the setting and emit a changed signal
// (we are not checking if the value really changed before emitting for simplicity)
QSettings::setValue(key, value);
emit settingChanged(key);
}
void Settings::setValueIfNotSet(const QString &key, const QVariant &value) {
// change the setting and emit a changed signal
if( !QSettings::contains(key) ) {
QSettings::setValue(key, value);
// (we are not checking if the value really changed before emitting for simplicity)
emit settingChanged(key);
}
}
void Settings::initToDefaults() {
setValueIfNotSet("test", true);
}