Accessing C++ QLists from QML
Solution 1
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:
- Fast - it doesn't copy the entire list to access one element.
- You can easily get animations for changes to the list (addition, rearrangement and removal of items).
- It's easy to use from QML.
- 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];
}
Solution 2
Alternatively, You can use QVariantList
(QList<QVariant>
), it will automatically change to JavaScript array when passed to QML, and it is read and write-able from C++ and QML
Solution 3
I came across this question while trying to fix a similar problem, where I wanted to use C++ code as a model source in QML. The answer given by TheBootroo pointed me in the right direction, but did not work fully for me. I do not have enough reputation to answer him directly (but I did upvote his answer).
I am using Qt 5.0.0 I found this link very helpful
The definition of ThingManager should be changed as follows
class ThingManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> things READ getThings NOTIFY thingsChanged)
public:
QList<QObject*> getThings () const { return m_things; }
signals:
void thingsChanged ();
private:
QList<QObject*> m_things;
};
Note that I changed the return type of getThings to a QList<QObject*>. Without this change, Qt warns that it is "Unable to handle unregistered datatype 'QList<Thing*>'".
In the QML code, the properties of Thing can be accessed through the model as model.modelData.size and model.modelData.name.
Solution 4
Ah I found the answer (I think, not tested): QQmlListProperty
There's a few uses in the examples, e.g. at qtdeclarative/examples/quick/tutorials/gettingStartedQml/filedialog/directory.*
:
Unfortunately you can only have read-only lists at the moment.
Solution 5
you are quite wrong about QObject, they can be given to a QList, simply in the form of a pointer, as following works perfectly :
class Thing : public QObject
{
Q_OBJECT
Q_PROPERTY (int size READ getSize CONSTANT)
Q_PROPERTY (QString name READ getName CONSTANT)
public:
Thing(QObject * parent = NULL) : QObject(parent) {}
int getSize () const { return m_size; }
QString getName () const { return m_name; }
private:
int m_size;
QString m_name;
};
class ThingManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<Thing*> things READ getThings NOTIFY thingsChanged)
public:
QList<Thing*> getThings () const { return m_things; }
signals:
void thingsChanged ();
private:
QList<Things*> m_things;
};
Timmmm
Updated on October 01, 2020Comments
-
Timmmm over 3 years
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 becauseQObject
s can't be put in aQList
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;