Writing property that contains QList (problem converting to QVariant)
-
Hello,
The following question applies to Qt 6. I'm trying to load properties from an xml file into a class using the QMetaProperty::write() function. To simplify the example I left out the xml part and just hardcoded some values that I would like to write to the properties in this class. I'm using QMetaProperty::write() instead of the set functions because it allows me to easily loop over all properties in a class. This works perfectly, except for two types: a list of a list of points (QList<QList<QPointF>>) and a list of rectangles (QList<QRectF>). Somehow Qt is unable to convert these types into a QVariant, even though I used Q_DECLARE_METATYPE(QList<QList<QPointF>>) and Q_DECLARE_METATYPE(QList<QRectF>). How can I solve this problem? Minimal example below.
listexample.h
#ifndef LISTEXAMPLE_H #define LISTEXAMPLE_H #include <QMetaProperty> #include <QRect> Q_DECLARE_METATYPE(QList<QList<QPointF>>); Q_DECLARE_METATYPE(QList<QRectF>); class ListExample : public QObject { Q_OBJECT Q_PROPERTY(QList<QList<QPointF>> series MEMBER m_series READ getSeries WRITE setSeries NOTIFY seriesChanged) Q_PROPERTY(QList<QRectF> axisLims MEMBER m_axisLims READ getAxisLims WRITE setAxisLims NOTIFY axisLimsChanged) public: explicit ListExample(); QList<QList<QPointF>> m_series; QList<QRectF> m_axisLims; Q_INVOKABLE QList<QList<QPointF>> getSeries() const; Q_INVOKABLE void setSeries(const QList<QList<QPointF>> series); Q_INVOKABLE QList<QRectF> getAxisLims() const; Q_INVOKABLE void setAxisLims(const QList<QRectF> axisLims); Q_INVOKABLE void deserializeObject(); signals: void seriesChanged(QList<QList<QPointF>> m_series); void axisLimsChanged(QList<QRectF> m_axisLims); }; #endif // LISTEXAMPLE_H
listexample.cpp
#include "listexample.h" ListExample::ListExample() : QObject() { } QList<QList<QPointF>> ListExample::getSeries() const { return m_series; } void ListExample::setSeries(const QList<QList<QPointF>> series) { if (series == m_series) return; m_series = series; emit seriesChanged(m_series); } QList<QRectF> ListExample::getAxisLims() const { return m_axisLims; } void ListExample::setAxisLims(const QList<QRectF> axisLims) { if (axisLims == m_axisLims) return; m_axisLims = axisLims; emit axisLimsChanged(m_axisLims); } void ListExample::deserializeObject() { const QMetaObject* metaObject = this->metaObject(); int numProperties = metaObject->propertyCount(); for (int propertyNr=0; propertyNr<numProperties; propertyNr++) { QMetaProperty property = metaObject->property(propertyNr); QString propertyType = QString(property.typeName()).remove(" "); QVariant propertyValue; // Normally loaded from xml, but hardcoded for example if (propertyType == "QList<QList<QPointF>>") { QList<QList<QPointF>> series({{QPointF(1,2), QPointF(3,4)}, {QPointF(5,6), QPointF(7,8)}}); propertyValue = QVariant(series); // DOES NOT COMPILE! propertyValue = series; // DOES NOT COMPILE! } else if (propertyType == "QList<QRectF>") { QList<QRectF> axisLims({QRectF(1,2,3,4), QRectF(5,6,7,8)}); propertyValue = QVariant(axisLims); // DOES NOT COMPILE! propertyValue = axisLims; // DOES NOT COMPILE! } if (!property.write(this, propertyValue)) { QString propertyName(property.name()); QString className(metaObject->className()); qDebug() << "Unable to write property " + propertyName + " to class " + className; } } }
CMakeLists.txt
cmake_minimum_required(VERSION 3.0 FATAL_ERROR) project(listexample) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 REQUIRED COMPONENTS Core) add_executable(listexample ${GUI_TYPE} listexample.h listexample.cpp) target_compile_definitions(listexample PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>) target_link_libraries(listexample PRIVATE Qt${QT_VERSION_MAJOR}::Core)
-
@Lotte said in Writing property that contains QList (problem converting to QVariant):
propertyValue = QVariant(series); // DOES NOT COMPILE!
Hi.
This would not compile becauseQVariant
does not have a constructor overload that takes aQList<QList<QPoint>>
orQList<QRectF>
as a parameter.I believe you need to do like that:
propertyValue = QVariant::fromValue(series);
Also, as mentioned above, consider passing parameters to the setter methods by constant reference, i.e.:
Q_INVOKABLE void setAxisLims(const QList<QRectF> &axisLims); // note the '&'
-
Hi and welcome to devnet,
Did you register them as well ?
-
Adding the following two lines to the ListExample constructor does not seem to make a difference. Code still does not compile. Any suggestions are very welcome...
qRegisterMetaType<QList<QList<QPointF>>>("QList<QList<QPointF>>"); qRegisterMetaType<QList<QRectF>>("QList<QList<QPointF>>");
-
Adding the following two lines to the ListExample constructor does not seem to make a difference. Code still does not compile. Any suggestions are very welcome...
qRegisterMetaType<QList<QList<QPointF>>>("QList<QList<QPointF>>"); qRegisterMetaType<QList<QRectF>>("QList<QList<QPointF>>");
@Lotte
Hi there. This may be totally unrelated to your current problem, so you may choose to ignore it for now. I also don't know anything about QML. But your definitions like:void ListExample::setSeries(const QList<QList<QPointF>> series) { if (series == m_series) return;
Because you have not used any
&
s for "reference" inQList<QList<QPointF>> series
that means it will copy theQList<QList<QPointF>>
passed in. And so even if you pass inm_series
as actual parameter I believeif (series == m_series)
will never be true. (You could check this to verify if I am correct.) Efficiency-wise you might want to rethink your parameter type here and insetAxisLims()
etc. -
@JonB Thanks for the reply. I tested your suggestion by adding the following 2 functions to the ListExample class.
void ListExample::compareSeries(const QList<QList<QPointF>> series1, const QList<QList<QPointF>> series2) { if (series1 == series2) qDebug() << "series are the same"; else qDebug() << "series are different"; } void ListExample::runTest() { QList<QList<QPointF>> seriesA({{QPointF(1,2), QPointF(3,4)}, {QPointF(5,6), QPointF(7,8)}}); QList<QList<QPointF>> seriesB({{QPointF(1,2), QPointF(3,4)}, {QPointF(5,6), QPointF(7,8)}}); QList<QList<QPointF>> seriesC({{QPointF(8,7), QPointF(6,5)}, {QPointF(4,3), QPointF(2,1)}}); compareSeries(seriesA, seriesB); compareSeries(seriesA, seriesC); compareSeries(seriesB, seriesC); }
The output for runTest() is:
series are the same series are different series are different
So the check in if (series == m_series) seems to be working correctly. However, the code still does not compile when uncommenting the problematic lines
propertyValue = QVariant(series); // DOES NOT COMPILE! propertyValue = series; // DOES NOT COMPILE!
and
propertyValue = QVariant(axisLims); // DOES NOT COMPILE! propertyValue = axisLims; // DOES NOT COMPILE!
Any more ideas? I'm completely stuck so anything is welcome!
-
@Lotte said in Writing property that contains QList (problem converting to QVariant):
propertyValue = QVariant(series); // DOES NOT COMPILE!
Hi.
This would not compile becauseQVariant
does not have a constructor overload that takes aQList<QList<QPoint>>
orQList<QRectF>
as a parameter.I believe you need to do like that:
propertyValue = QVariant::fromValue(series);
Also, as mentioned above, consider passing parameters to the setter methods by constant reference, i.e.:
Q_INVOKABLE void setAxisLims(const QList<QRectF> &axisLims); // note the '&'
-
@JonB Thanks for the reply. I tested your suggestion by adding the following 2 functions to the ListExample class.
void ListExample::compareSeries(const QList<QList<QPointF>> series1, const QList<QList<QPointF>> series2) { if (series1 == series2) qDebug() << "series are the same"; else qDebug() << "series are different"; } void ListExample::runTest() { QList<QList<QPointF>> seriesA({{QPointF(1,2), QPointF(3,4)}, {QPointF(5,6), QPointF(7,8)}}); QList<QList<QPointF>> seriesB({{QPointF(1,2), QPointF(3,4)}, {QPointF(5,6), QPointF(7,8)}}); QList<QList<QPointF>> seriesC({{QPointF(8,7), QPointF(6,5)}, {QPointF(4,3), QPointF(2,1)}}); compareSeries(seriesA, seriesB); compareSeries(seriesA, seriesC); compareSeries(seriesB, seriesC); }
The output for runTest() is:
series are the same series are different series are different
So the check in if (series == m_series) seems to be working correctly. However, the code still does not compile when uncommenting the problematic lines
propertyValue = QVariant(series); // DOES NOT COMPILE! propertyValue = series; // DOES NOT COMPILE!
and
propertyValue = QVariant(axisLims); // DOES NOT COMPILE! propertyValue = axisLims; // DOES NOT COMPILE!
Any more ideas? I'm completely stuck so anything is welcome!
@Lotte said in Writing property that contains QList (problem converting to QVariant):
So the check in if (series == m_series) seems to be working correctly.
Just on that one. I forgot that
QList
has Qt's only-copy-on-write semantics, hence why it works.
But agree this is not related to your issue anyway.