Issues with QVariantLists/QLists of custom QObjects



  • I'm hoping to get some help with a snag I've run into on a personal project. Essentially I have a custom QObject inheritor that contains its own various elements of data, and another custom QObject that I would like to have some collection element of multiple instances of the other custom QObject. I've tried various ways of implementing this and thus far I've had no real success, the closest being with a QVariantList. The following snippets are from an example I wrote to recreate the issue, but they follow the same general structure as their more detailed counterparts.

    Data represents the initial custom QObject.

    "data.h"

    @#ifndef DATA_H
    #define DATA_H
    #include <QObject>

    class Data : public QObject
    {

    Q_OBJECT
    public:
    explicit Data(QObject *parent = 0);
    Data(const Data &other);
    ~Data();
    QString getName();
    QString getContent();
    void setName(QString newName);
    void setContent(QString newContent);

    private:
    QString name, content;
    };

    Q_DECLARE_METATYPE(Data)

    #endif // DATA_H@

    "data.cpp"

    @#include "data.h"

    Data::Data(QObject *parent) :
    QObject(parent)
    {
    name = "testData";
    content = "testContent";
    }

    Data::Data(const Data &other){}

    Data::~Data(){}

    QString Data::getName(){return name;}

    QString Data::getContent(){return content;}

    void Data::setName(QString newName){name = newName;}

    void Data::setContent(QString newContent){content = newContent;}@

    TestContainer is the object which will hold some collection of Data

    "testcontainer.h"

    @#ifndef TESTCONTAINTER_H
    #define TESTCONTAINER_H

    #include <QObject>
    #include <QString>
    #include <QVariantList>
    #include "data.h"

    class TestContainer : public QObject
    {
    Q_OBJECT
    public:
    explicit TestContainer(QObject *parent = 0);
    TestContainer(const TestContainer &other);
    Q_INVOKABLE QVariantList getList();

    private:
    QString name;
    QVariantList theList;

    };

    Q_DECLARE_METATYPE(TestContainer)

    #endif // TESTCONTAINTER_H@

    "testcontainer.cpp"

    @#include "testcontainer.h"

    TestContainer::TestContainer(QObject *parent) :
    QObject(parent)
    {
    name = "test container";
    Data temp;
    theList.append(QVariant::fromValue(temp));
    }

    TestContainer::TestContainer(const TestContainer &other){
    name = other.name;
    theList = other.theList;
    }

    QVariantList TestContainer::getList(){return theList;}@

    "main.cpp"

    @#include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>

    #include "testcontainer.h"

    int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);

    qRegisterMetaType<Data>("Data");
    qRegisterMetaType<TestContainer>("TestContainer");
    //qmlRegisterType<Data>("custom.data", 1, 0, "Data");
    //qmlRegisterType<TestContainer>("custom.testcontainer", 1, 0, "TestContainer");

    QQmlApplicationEngine engine;

    TestContainer theContainer;
    engine.rootContext()->setContextProperty("theContainer", &theContainer);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
    }@

    "main.qml"

    @import QtQuick 2.3
    import QtQuick.Controls 1.2
    //import custom.data 1.0
    //import custom.testcontainer 1.0

    ApplicationWindow {
    visible: true
    width: 320
    height: 240
    title: qsTr("Testing Functionality")

    Text {
    text: theContainer.getList()[0].getName();
    anchors.centerIn: parent
    }
    }@

    Put together all of this provides me with a nice:

    "TypeError: Property 'getName' of object QVariant(Data) is not a function"

    Thoughts?



  • This setup is not going to work. QObject derived classes are not copyable. In order to insert them into a container, they need to be copyable. QObject are shared as pointers, not as values.



  • So you're telling me there is no way to expose any sort of container of QObject inheritors to QML?

    What's the best course of action here then? To my understanding in order to expose any properties of an object to QML it has to be a QObject. Is this not the case? How do I take a collection of custom objects and expose them?



  • You can expose pointers to objects, but not the objects themselves. This is basic Qt. QObjects cannot be copied, period. Note that QML handles pointers to objects just fine. All objects in QML really are pointers in the background.



  • What is your suggestion then? How does one expose a container of QObject-derived items or rather a container of pointers to QObject-derived items? I've attempted to use QList<QObject*> but have been met with similar issues.



  • After hours of research and more trial and error I've finally discovered a solution that works for me. Rather than trying to force a QList<Object*> or QVariantList to function correctly I managed to get everything working with QList<Data*> and QQmlListProperty<Data>. Currently I am able to both use the QList as a model for things like TableView and am also able to access individual elements of the list. For models it goes something like:

    @model: testContainer.theList@

    And to access an individual element something like:

    @text: theContainer.getData(0).name@

    The following is the current code that achieves this:

    "data.h"

    @#ifndef DATA_H
    #define DATA_H

    #include <QObject>

    class Data : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QString name READ getName WRITE setName)
    Q_PROPERTY(QString content READ getContent WRITE setContent)
    public:
    explicit Data(QObject *parent = 0);
    QString getName();
    QString getContent();
    void setName(QString newName);
    void setContent(QString newContent);

    private:
    QString name, content;
    };

    #endif // DATA_H@

    "data.cpp"

    @#include "data.h"

    Data::Data(QObject *parent) :
    QObject(parent){}

    QString Data::getName(){return name;}

    QString Data::getContent(){return content;}

    void Data::setName(QString newName){name = newName;}

    void Data::setContent(QString newContent){content = newContent;}@

    "testContainer.h"

    @#ifndef TESTCONTAINTER_H
    #define TESTCONTAINER_H

    #include <QObject>
    #include <QList>
    #include <QQmlListProperty>

    #include "data.h"

    class TestContainer : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Data> theList READ getList)
    public:
    explicit TestContainer(QObject parent = 0);
    QQmlListProperty<Data> getList();
    void addData(Data
    d);
    Q_INVOKABLE Data* getData(int i);

    private:
    QString name;
    QList<Data*> theList;
    };

    #endif // TESTCONTAINTER_H@

    "testContainer.cpp"

    @#include "testcontainer.h"

    TestContainer::TestContainer(QObject *parent) :
    QObject(parent){}

    QQmlListProperty<Data> TestContainer::getList(){
    return QQmlListProperty<Data>(this, theList);}

    void TestContainer::addData(Data* d){theList.append(d);}

    Data* TestContainer::getData(int i){return theList.at(i);}@

    "main.cpp"

    @#include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include <QTQml>

    #include "testcontainer.h"

    int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);

    qmlRegisterType<Data>("Custom", 1, 0, "Data");
    qmlRegisterType<TestContainer>("Custom", 1, 0, "TestContainer");
    
    QQmlApplicationEngine engine;
    
    TestContainer theContainer;
    Data d1;
    d1.setName("test name 1");
    d1.setContent("test content 1");
    Data d2;
    d2.setName("test name 2");
    d2.setContent("test content 2");
    theContainer.addData(&d1);
    theContainer.addData(&d2);
    engine.rootContext()->setContextProperty("theContainer", &theContainer);
    
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    return app.exec();
    

    }@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.