Solved passing a list of custom objects to QML
-
First let me tell you what my overall goal is: to pass a list (vector, array or whatever) of custom C++ objects into QML. I've been able to do this successfully (as in it compiles, runs and gives the result I want), but it's possible I'm doing something that I shouldn't. Also it's not clear to me why my solution works and not another one. Here is some code (omitting includes and some other stuff that isn't really necessary to the concept):
testqobject.h
class TestQObject : public QObject { Q_OBJECT Q_PROPERTY(QString foo MEMBER foo) public: explicit TestQObject(QObject* parent = nullptr); TestQObject(const QString& _foo); TestQObject(const TestQObject& rhs); TestQObject& operator=(const TestQObject& rhs); Q_INVOKABLE static QVariantList get_foos(); Q_INVOKABLE static QVariantList get_bars(); signals: public slots: private: QString foo; }; Q_DECLARE_METATYPE(TestQObject); // Constructors etc were here.... QVariantList TestQObject::get_foos() { QVariantList ret; ret << QVariant::fromValue(TestQObject("foo")) << QVariant::fromValue(TestQObject("baz")); return ret; } QVariantList TestQObject::get_bars() { QVariantList ret; ret << QVariant::fromValue(TestQGadget("bar")) << QVariant::fromValue(TestQGadget("baz")); return ret; }
testqgadget.h
class TestQGadget : public QObject { Q_GADGET Q_PROPERTY(QString bar MEMBER bar) public: TestQGadget(const QString& _bar = QString()); TestQGadget(const TestQGadget& rhs); ~TestQGadget(); TestQGadget& operator=(const TestQGadget& rhs); signals: public slots: private: QString bar; }; Q_DECLARE_METATYPE(TestQGadget); // Constructors etc. were here....
QObject* make_testqobject(QQmlEngine* qml_engine, QJSEngine* js_engine) { return new TestQObject(); } int main(int argc, char* argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<TestQObject>("com.example.test", 1, 0, "TestQObject"); qmlRegisterType<TestQGadget>("com.example.test", 1, 0, "TestQGadget"); qmlRegisterSingletonType<TestQObject>("com.example.test", 1, 0, "TestQObjectSingle", &make_testqobject); // and so on }
main.qml
import QtQuick 2.9 import QtQuick.Window 2.2 import com.example.test 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Q_OBJECT/Q_GADGET") Component.onCompleted: { var foos = TestQObjectSingle.get_foos(); for(var i = 0; i < foos.length; ++i) { console.log(foos[i]); console.log(foos[i].foo); } var bars = TestQObjectSingle.get_bars(); for(i = 0; i < bars.length; ++i) { console.log(bars[i]); console.log(bars[i].bar); } } }
output is
qml: QVariant(TestQObject) qml: undefined qml: QVariant(TestQObject) qml: undefined qml: TestQGadget(bar) qml: bar qml: TestQGadget(baz) qml: baz
Sorry if the static functions in TestQObject are confusing, in the real thing I'm trying to do I get the list of objects from a different class, I just wanted to try to get everything into a minimal example. So my main question is why doesn't the TestQObject (which uses Q_OBJECT) list work (i.e. why doesn't the "foo" property get accessed) and is it possible to make it work? The second question is, I tried using Q_GADGET like in TestQGadget (there's not a whole lot of helpful documentation I could find on Q_GADGET) and for some reason if I don't inherit from QObject it won't compile, even though the examples I saw Q_GADGET classes weren't ever inheriting from anything. So the second question is, what would be the proper way to use the Q_GADGET version? And finally, if both Q_OBJECT and Q_GADGET are possible, which way would be better?
-
@jacraig said in passing a list of custom objects to QML:
I tried using Q_GADGET like in TestQGadget (there's not a whole lot of helpful documentation I could find on Q_GADGET) and for some reason if I don't inherit from QObject it won't compile
Remove
signals
andslots
from your header. Q_GADGET does not support them - hence you were getting compilation errors.If you want to return a list of objects that QML will understand, use a QObjectList.
-
@sierdzio said in passing a list of custom objects to QML:
Remove signals and slots from your header. Q_GADGET does not support them - hence you were getting compilation errors.
This does not seem to help. Still get the same compile error (when QObject inheritance is removed):
/usr/include/qt5/QtQml/qqmlprivate.h:101:9: error: ‘QQmlPrivate::QQmlElement<T>::~QQmlElement() [with T = TestQGadget]’ marked ‘override’, but does not override ~QQmlElement() override { ^
So I see QObjectList is a typedef for QList<QObject*>. I assume these pointers should be created on the heap. Can a QML object be their parent? In my case, no object on the C++ side would currently make sense as the parent of these items.
-
@jacraig said in passing a list of custom objects to QML:
So I see QObjectList is a typedef for QList<QObject*>. I assume these pointers should be created on the heap. Can a QML object be their parent? In my case, no object on the C++ side would currently make sense as the parent of these items.
Yes it can, although setting in from C++ side is a bit cumbersome (entirely possible, though: find any children of your QQmlEngine::context instance).
It is also 100% possible and legal to keep QObjects without a parent. The only downside of that is you have to remember to delete them yourself.
-
Just to bring this up to date, returning a QList<QObject*> does work to send a list of objects to QML. Note however that the QObjectList typedef did not work--QML complained when a function with that return type was attempted to be invoked.
Thanks!