PDA

View Full Version : Serialising QList<int> with QSettings



agarny
25th February 2014, 17:14
Hi,

I am trying, using QSettings, to serialise various settings, which are of type QList<int>. Now, it all works fine on Windows and OS X, but for some reasons it doesn't on Linux. Here is how I do it to load (https://github.com/opencor/opencor/blob/653bf65ea4bbd4e9819f4d5d43cabf8867c59f42/src/plugins/editing/CellMLAnnotationView/src/cellmlannotationviewwidget.cpp#L77) and save (https://github.com/opencor/opencor/blob/653bf65ea4bbd4e9819f4d5d43cabf8867c59f42/src/plugins/editing/CellMLAnnotationView/src/cellmlannotationviewwidget.cpp#L99) some of those settings. Notice that I register QList<int> as a meta-type (using qRegisterMetaTypeStreamOperators), so I believe I should be all fine and, indeed, it all works as I would expect on Windows and OS X, but not on Linux. On Linux, I get a bunch of warnings like:


QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.
QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::save: unable to save type 'QList<int>' (type id: 1038).

QVariant::load: unable to load type 1038.
QVariant::load: unable to load type 1038.

Just out of curiosity, I thought I would try loading/saving my settings in a different way by using QVariantList. Thus, for loading my settings, I have something like:


void CellmlAnnotationViewWidget::loadSettings(QSettings *pSettings)
{
// Retrieve the sizes of our editing widget and of its metadata details
// Note: we would normally do this in CellmlAnnotationViewEditingWidget, but
// we have one instance of it per CellML file and we want to share
// some information between the different instances, so we have to do
// it here instead...

QVariantList defaultEditingWidgetSizes = QVariantList() << 0.25*qApp->desktop()->screenGeometry().width()
<< 0.75*qApp->desktop()->screenGeometry().width();
QVariantList defaultMetadataDetailsWidgetSizes = QVariantList() << 0.25*qApp->desktop()->screenGeometry().height()
<< 0.25*qApp->desktop()->screenGeometry().height()
<< 0.50*qApp->desktop()->screenGeometry().height();

QVariantList editingWidgetSizes = pSettings->value(SettingsCellmlAnnotationViewEditingWidgetSiz es, defaultEditingWidgetSizes).toList();
QVariantList metadataDetailsWidgetSizes = pSettings->value(SettingsCellmlAnnotationViewMetadataDetailsW idgetSizes, defaultMetadataDetailsWidgetSizes).toList();

foreach (const QVariant &editingWidgetSize, editingWidgetSizes)
mEditingWidgetSizes << editingWidgetSize.toInt();

foreach (const QVariant &metadataDetailsWidgetSize, metadataDetailsWidgetSizes)
mMetadataDetailsWidgetSizes << metadataDetailsWidgetSize.toInt();
}

while for saving them, I have something like:


void CellmlAnnotationViewWidget::saveSettings(QSettings *pSettings) const
{
// Keep track of the sizes of our editing widget and of its metadata details

QVariantList editingWidgetSizes = QVariantList();
QVariantList metadataDetailsWidgetSizes = QVariantList();

foreach (const int &editingWidgetSize, mEditingWidgetSizes)
editingWidgetSizes << editingWidgetSize;

foreach (const int &metadataDetailsWidgetSize, mMetadataDetailsWidgetSizes)
metadataDetailsWidgetSizes << metadataDetailsWidgetSize;

pSettings->setValue(SettingsCellmlAnnotationViewEditingWidget Sizes, editingWidgetSizes);
pSettings->setValue(SettingsCellmlAnnotationViewMetadataDetai lsWidgetSizes, metadataDetailsWidgetSizes);
}

Now, the interesting bit is that... it works on Windows, OS X and... also Linux! So, could it be that I have just come across a bug with Qt (5.2.1) on Linux?...

Whatever the case, even though using QVariantList does indeed 'fixes' my problem, I would rather use my original approach, if possible (I find 'cleaner'). This aside, I am curious as what I have done wrong, if anything, in my original approach. Anyone?

Cheers, Alan.

anda_skoa
25th February 2014, 17:27
Hmm. Can you check if QMetaType::save/load work for your type?

Cheers,
_

agarny
25th February 2014, 18:05
Ok, I have just tested using the following code:


int id = QMetaType::type("QList<int>");

if (id == QMetaType::UnknownType) {
std::cout << ">>> Unknown type!?" << std::endl;
} else {
QList<int> data = QList<int>() << 3 << 5 << 7;
QList<int> readData = QList<int>();

QByteArray byteArray;
QDataStream dataStream(&byteArray, QIODevice::WriteOnly);
QDataStream readDataStream(&byteArray, QIODevice::ReadOnly);

QMetaType::save(dataStream, id, &data);
QMetaType::load(readDataStream, id, &readData);

bool error = false;

if (data.size() != readData.size()) {
std::cout << ">>> data.size() != readData.size()" << std::endl;
std::cout << " " << data.size() << " vs. " << readData.size() << std::endl;

error = true;
} else {
for (int i = 0, iMax = data.size(); i < iMax; ++i)
if (data[i] != readData[i]) {
std::cout << ">>> data[" << i << "] != readData[" << i << "]" << std::endl;
std::cout << " " << data[i] << " vs. " << readData[i] << std::endl;

error = true;

break;
}
}

if (!error)
std::cout << ">>> Everything went fine..." << std::endl;
}

and I got:


>>> Everything went fine...

on Windows, OS X and Linux. So, as far as I can tell, QMetaType::save/load works fine for my QList<int> type.

anda_skoa
25th February 2014, 20:17
I had a quick look into the code and it seems to be more a limitation of the format rather than the platform.

When writing the content to an INI file, the code checks for QStringList or QVariantList and serializes that into a list.
Anything else is serialized as a string.

So the stream operators are not used at all.

You could try to register a converter (QMetaType::registerConverter) that serializes the list into a string.
Not sure if the same trick works the other way around though.

Cheers,
_

agarny
26th February 2014, 09:08
I had a quick look into the code and it seems to be more a limitation of the format rather than the platform.

When writing the content to an INI file, the code checks for QStringList or QVariantList and serializes that into a list.
Anything else is serialized as a string.

So the stream operators are not used at all.

I must confess that I haven't checked the code, but... how would you explain that it works fine on Windows and OS X, but not on Linux?

anda_skoa
26th February 2014, 11:51
I must confess that I haven't checked the code, but... how would you explain that it works fine on Windows and OS X, but not on Linux?

My guess was that you were not using the IniFormat on all platforms, e.g. using NativeFormat everywhere which would be IniFormat only on non-Mac Unix.
But if you are using IniFormat on all platforms then this is certainly weird and a bug.

Cheers,
_

agarny
26th February 2014, 13:35
My guess was that you were not using the IniFormat on all platforms, e.g. using NativeFormat everywhere which would be IniFormat only on non-Mac Unix.
But if you are using IniFormat on all platforms then this is certainly weird and a bug.

I had forgotten that QSettings can be used with different formats. I personally use the default format, i.e. NativeFormat, but I thought I would try with IniFormat. I did my testing on OS X and, as on Linux, my settings don't get loaded/saved 'properly'. Keeping in mind what you said about "the code [checking] for QStringList or QVariantList and [serializing] that into a list. Anything else [being] serialized as a string", it now all makes sense.

So, everything works as expected. As for my code, I just ended up using QVariantList rather than QList<int>, and now everything works fine on all three platforms.