c++qmlqt5qlistqtquick2

Accessing C++ QLists from QML


If I've got a list of things in C++, how do I expose that to QML (in Qt5 / QtQuick 2)? It seems like QML can only understand QObject-derived classes, which is an issue because QObjects can't be put in a QList or copied. How do I do this:

struct Thing
{
    int size;
    QString name;
};

class ThingManager : public QObject
{
    Q_OBJECT

    // These macros support QtQuick, in case we one day want to use it to make a slick
    // interface (when QML desktop components are released).
    Q_PROPERTY(QList<Thing> things READ things NOTIFY thingssChanged)

public:
    // ...
    QList<Thing> things() const;

    // ...

};

So that I can do something like this in QML:?

var a = thingManager.things[0].name;

Solution

  • After more experience with QML I've found the best way to have lists of things is with a QAbstractListModel.

    You make your Thing derive from QObject so it can be stored in a QVariant (after registering it). Then you can return the actual Thing as the model item. You can access it in a Repeater as model.display.a_property_of_thing. The list length is available as model.count.

    This has the following pros and cons:

    1. Fast - it doesn't copy the entire list to access one element.
    2. You can easily get animations for changes to the list (addition, rearrangement and removal of items).
    3. It's easy to use from QML.
    4. To enable the animations to work, whenever you change the list you have to do some slightly faffy bookkeeping (beginInsertRows() etc.)

    ...

    class Things : public QObject
    {
    ...
    };
    
    Q_DECLARE_METATYPE(Thing*)
    
    class ThingList : public QAbstractListModel
    {
        Q_OBJECT
        
    public:
        explicit ThingList(QObject *parent = 0);
        ~ThingList();
    
        int rowCount(const QModelIndex& parent = QModelIndex()) const override;
        QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    
    public slots:
    
        // Extra function to get the thing easily from outside Repeaters.
        Thing* thing(int idx);
    
    private:
        QList<Thing*> mThings;
    };
    
    int ThingList::rowCount(const QModelIndex& parent) const
    {
        return mThings.size();
    }
    
    QVariant ThingList::data(const QModelIndex& index, int role) const
    {
        int i = index.row();
        if (i < 0 || i >= mThings.size())
            return QVariant(QVariant::Invalid);
    
        return QVariant::fromValue(mThings[i]);
    }
    
    Thing* ThingList::thing(int idx)
    {
        if (idx < 0 || idx >= mThings.size())
            return nullptr;
    
        return mThings[idx];
    }