3
votes

It is made clear in the docs that the C++ class that has the list must inherit from QObject but it's not clear if the objects inside the list must be QObjects.
I have tried this approach with a QQmlListProperty and also with a QQmlListProperty, but I get the following errors in both cases:

QMetaProperty::read: Unable to handle unregistered datatype 'QQmlListProperty<QString>' for property 'ns::Status::values'

Edit: My code:

.hpp:

namespace ns{
class Status : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<QString> values READ values NOTIFY valuesChanged)

public:
    Status(QObject* parent = Q_NULLPTR);
    virtual ~Status();

    bool updateValues(const std::vector<std::string>& values);

    QQmlListProperty<QString> values();
    int valueCount() const;
    QString* value(int) const;

signals:
    void valuesChanged();

private:
    QVector<QString*> m_values_;

    std::vector<QString> m_valueStorage_;

    static int valueCount(QQmlListProperty<QString>*);
    static QString* value(QQmlListProperty<QString>*, int);
};
}

.cpp:

using namespace ns;
Status::Status(QObject* parent) :
    QObject(parent),
    m_values_(2),
    m_valueStorage_(2)
{}

Status::~Status(){}

bool Status::updateValues(const std::vector<std::string>& values)
{
    //Do Stuff

    emit valuesChanged();
    return true;
}

QQmlListProperty<QString> Status::values()
{
    return QQmlListProperty<QString>(this, nullptr, &Status::valueCount, &Status::value);
}

int Status::valueCount() const
{
    return m_values_.count();
}

QString* Status::value(int index) const
{
    return m_values_.at(index);
}

int Status::valueCount(QQmlListProperty<QString>* list){
    return reinterpret_cast<Status*>(list->data)->valueCount();
}

QString* Status::value(QQmlListProperty<QString>* list, int i){
    return reinterpret_cast<Status*>(list->data)->value(i);
}

QML:

import Foo 1.0
Rectangle {
    Status {
        id: status
        objectName: "Status"
    }

    Component {
        id: value
        Rectangle { Text: {text: modelData } }
    }

    ColumnLayout {
        Repeater { model: status.values; delegate: value }
    }
}

Also, in my main, before I load the qml file into the QQmlApplicationEngine:

qmlRegisterType<ns::Status>("Foo", 1,0, "Status");
1
Show your code.Azeem
@Azeem Edited my question.CK.
Keep it mind that the intended usage of QQmlListProperty is to expose a list that can be populated from the QML side. Not to expose c++ data to QML. If you want to do that, a QAbstractListModel will suit you better.GrecKo
Oh, it also makes sense because we need a model for the Repeater?CK.
@GrecKo I've never come across that in the documentations. Can you link a page which can explain the difference a little better?CK.

1 Answers

6
votes

QQmlListProperty is indeed meant to be used with QObject.

Although there's nothing in the class enforcing this limit since it's all templated, the QML engine expect the element type to be a subclass of QObject.

An indication in the Qt documentation that this is the case can be found in the doc of QQmlListReference(a class made to manipulate QQmlListProperty from c++). This paragraph mentions QObject :

QML list properties are type-safe. Only QObject's that derive from the correct base class can be assigned to the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported. Attempting to add objects of the incorrect type to a list property will fail.

The QObject *QQmlListReference::at(int index) const method is also another clue.