Save QSet using QSettings
-
I need to have a list of unique strings, for which I prefer to use
QSet
, because I also need to do searches. However, I'd like to save this list saved in settings, i.e. useQSettings
to save it. However, there's noQSet
=>QVariant
conversion so I'm stuck.[Edit: code tags, typos ~~ @Wieland]
-
this is what i did:
void Game::readSettings() { QSettings settings { settingsFile, QSettings::IniFormat }; const auto usernames = settings.value("usernames"); m_usernames = usernames.value<QSet<QString>>(); } void Game::writeSettings() const { QSettings settings { settingsFile, QSettings::IniFormat }; QVariant varUsernames; varUsernames.setValue(m_usernames); settings.setValue("usernames", varUsernames); }
but i get runtime error on variant conversion (can't convert types)
-
i guess i'll just use
QStringList
... -
well, and another question i have, is...
i read the settings file from the "main" class where i have all my widgets. however, every widget has some specific properties that other widgets don't. and i want to read that property from the settings file. is it considered a good practice to read the same settings file from a few places?
-
Yes, it is what QSettings is made for.
From Qt doc: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;
-
@mpergand said in Save QSet using QSettings:
QVariant::load: unable to load type 1092
You have to implement the corresponding stream operators see qRegisterMetaTypeStreamOperators
-
@mpergand said in Save QSet using QSettings:
QVariant::load: unable to load type 1092
You have to implement the corresponding stream operators see qRegisterMetaTypeStreamOperators
thanks, that's what i needed.
this is how i did it. i set breakpoints to on operator << and >> to see how they work, but i can't spot the mistake. when i read the settings, i.e
writeSettings()
function, i get one more invalid entry read from in-stream. of course i can check its validity but i wanna understand where it comes from:void HighScores::readSettings() { QSettings settings { m_sSettingsFile, QSettings::IniFormat }; const auto highScores = settings.value("highScores"); if(highScores.isValid()) m_highScores = highScores.value<QVariantMultiMap_t>(); else m_highScores = QVariantMultiMap_t {}; } void HighScores::writeSettings() const { QSettings settings { m_sSettingsFile, QSettings::IniFormat }; settings.setValue("highScores", qVariantFromValue(m_highScores)); } QDataStream &operator<<(QDataStream &out, const HighScores::QVariantMultiMap_t &mmap) { const auto mmapEnd = mmap.constEnd(); for(auto it = mmap.constBegin(); it != mmapEnd; ++it) out << it.key() << it.value(); return out; } QDataStream &operator>>(QDataStream& in, HighScores::QVariantMultiMap_t& mmap) { QVariant key; QString value; while(!in.atEnd()) { in >> key >> value; mmap.insert(key, value); } return in; }
where
using QVariantMultiMap_t = QMultiMap<QVariant, QString>; QMultiMap<QVariant, QString> m_highScores;
i've put
Q_DECLARE_METATYPE(HighScoresPage::QVariantMultiMap_t)
in header file after class declaration, andqRegisterMetaTypeStreamOperators<QVariantMultiMap_t>("QVariantMultiMap_t");
in constructorwhich part am i doing wrong?
-
I found a similar topic with a clear anwser :
[https://forum.qt.io/topic/78161/custom-class-serialize-with-qdatastream](link url)I made a test with a QSet<QString>
typedef QSet<QString> QStringSet; Q_DECLARE_METATYPE(QStringSet) int main(int argc, char *argv[]) { qRegisterMetaTypeStreamOperators<QStringSet>("QStringSet"); QString home=QDir::homePath(); QString iniFileName="config.ini"; QString iniPath=home+"/"+iniFileName; QSettings settings(iniPath,QSettings::IniFormat); QStringSet setIn; QVariant varIn; setIn<<"A"<<"B"<<"C"; // save varIn.setValue(setIn); settings.setValue("Set",varIn); // load QVariant varOut=settings.value("Set"); QStringSet setOut=varOut.value<QStringSet>(); qDebug()<<"in "<<setIn; qDebug()<<"out"<<setOut; return 0; }
For QSet, no << operator needed, just to declare the type.
All seems to work and the ini file looks good. -
my code actually works, but not correctly
the part that goes wrong in my code is in
while
loop inQDataStream &operator>>(QDataStream& in, HighScores::QVariantMultiMap_t& mmap)
. i check the stream usingwhile(!in.atEnd()) { // read from stream }
when i have, say, 1 element in the container, the saving of it in stream is done correctly. however, when reading from the stream, the execution enter the loop one time, and then one more extra time and i get an invalid element saved in container
p.s.
i tried withQStringSet
but with this things are worse, writing to settings is okay, but reading always returnsQVariant::Invalid
:void UsernamePage::readSettings() { QSettings settings { filePath, QSettings::IniFormat }; const auto usernames = settings.value("usernames"); // <-- returns Invalid if(usernames.isValid()) m_usernames = usernames.value<QStringSet_t>(); else m_usernames = QStringSet_t {}; } void UsernamePage::writeSettings() const { QSettings settings { filePath, QSettings::IniFormat }; settings.setValue("usernames", qVariantFromValue(m_usernames)); } QDataStream &operator<<(QDataStream &out, const ChooseUsernamePage::QStringSet_t &sset) { const auto ssetEnd = sset.constEnd(); for(auto it = sset.constBegin(); it != ssetEnd; ++it) out << *it; return out; } QDataStream &operator>>(QDataStream &in, ChooseUsernamePage::QStringSet_t &sset) { QString username; while(!in.atEnd()) { in >> username; sset.insert(username); } return in; }
-
If you are using QSet or QMap, you don't need to implement the << operator cause it's alreday the case. You only need to declare the type.
Example with a QMap:
typedef QMap<QString,QVariant> Map; qRegisterMetaTypeStreamOperators<Map>("Map"); Map mapIn; QVariant varIn; mapIn["string"]="Hello"; mapIn["num"]=1000; // save varIn.setValue(mapIn); settings.setValue("Map",mapIn); // load varOut=settings.value("Map"); Map mapOut=varOut.value<Map>(); qDebug()<<"in "<<mapIn; qDebug()<<"out"<<mapOut;
Logs:
in QMap(("num", QVariant(int, 1000))("string", QVariant(QString, "Hello"))) out QMap(("num", QVariant(int, 1000))("string", QVariant(QString, "Hello")))
You only need to implement the stream ops for your custom classes.
[edit] QMap is a wrong example because it's already managed by QVariant :)
It's only needed fot QSet !
-
If you are using QSet or QMap, you don't need to implement the << operator cause it's alreday the case. You only need to declare the type.
Example with a QMap:
typedef QMap<QString,QVariant> Map; qRegisterMetaTypeStreamOperators<Map>("Map"); Map mapIn; QVariant varIn; mapIn["string"]="Hello"; mapIn["num"]=1000; // save varIn.setValue(mapIn); settings.setValue("Map",mapIn); // load varOut=settings.value("Map"); Map mapOut=varOut.value<Map>(); qDebug()<<"in "<<mapIn; qDebug()<<"out"<<mapOut;
Logs:
in QMap(("num", QVariant(int, 1000))("string", QVariant(QString, "Hello"))) out QMap(("num", QVariant(int, 1000))("string", QVariant(QString, "Hello")))
You only need to implement the stream ops for your custom classes.
[edit] QMap is a wrong example because it's already managed by QVariant :)
It's only needed fot QSet !
@mpergand i still get invalid qvariant :(
-
My Example of HighScores implementation.
I'm using a custom class Gamer:struct Gamer { QString name; int highScore; }; Q_DECLARE_METATYPE(Gamer) QDataStream &operator<<(QDataStream &out, const Gamer &gamer) { out<<gamer.name<<gamer.highScore; return out; } QDataStream &operator>>(QDataStream &in, Gamer &gamer) { in>>gamer.name; in>>gamer.highScore; return in; } // for qDebug logs QDebug operator<<(QDebug debug, const Gamer &gamer) { debug<<gamer.name<<", "<<gamer.highScore; return debug; }
Saving HighScores in a QList<Gamer>:
using HighScores=QList<Gamer>; qRegisterMetaTypeStreamOperators<HighScores>("HighScores"); Gamer g1{"John", 1200}; Gamer g2{"Irvin", 2200}; Gamer g3{"Lisa", 2140}; HighScores scores; scores<<g1<<g2<<g3; QVariant v; v.setValue(scores); settings.setValue("HighScores",v); // load v=settings.value("HighScores"); scores=v.value<HighScores>(); qDebug()<<scores;
Output of scores:
("John", 1200, "Irvin", 2200, "Lisa", 2140)
-
My Example of HighScores implementation.
I'm using a custom class Gamer:struct Gamer { QString name; int highScore; }; Q_DECLARE_METATYPE(Gamer) QDataStream &operator<<(QDataStream &out, const Gamer &gamer) { out<<gamer.name<<gamer.highScore; return out; } QDataStream &operator>>(QDataStream &in, Gamer &gamer) { in>>gamer.name; in>>gamer.highScore; return in; } // for qDebug logs QDebug operator<<(QDebug debug, const Gamer &gamer) { debug<<gamer.name<<", "<<gamer.highScore; return debug; }
Saving HighScores in a QList<Gamer>:
using HighScores=QList<Gamer>; qRegisterMetaTypeStreamOperators<HighScores>("HighScores"); Gamer g1{"John", 1200}; Gamer g2{"Irvin", 2200}; Gamer g3{"Lisa", 2140}; HighScores scores; scores<<g1<<g2<<g3; QVariant v; v.setValue(scores); settings.setValue("HighScores",v); // load v=settings.value("HighScores"); scores=v.value<HighScores>(); qDebug()<<scores;
Output of scores:
("John", 1200, "Irvin", 2200, "Lisa", 2140)
@mpergand that works but something's wrong with my code i guess?
-
i have a deadline so for now i'll use
QStringList
instead ofQSet<Qstring>
. after it i'll (hopefully) try to find what was the problem