So I have this idea of using Q_PROPERTY
s of QObjects instead of role names of QAbstractListModel
to have notifiable properties be exposed to QML.
My question is if this a good practice, because it feels a bit unatural for using with classes inherited from QAbstractItemModel
.
Let me explain this in a more detail.
So, recommended way of creating C++ model for QML is:
- Inherit
QAbstractListModel
(or others) for creating your custom model. - Overload
rowCount()
- Define your own data roles.
e.g:
enum DataRoles {
Name = Qt::UserRole + 1,
Description,
CustomData
};
...
QHash<int, QByteArray> TestList::roleNames() const
{
QHash<int, QByteArray> res;
res[static_cast<int>(DataRoles::Name)] = "name";
res[static_cast<int>(DataRoles::Description)] = "description";
res[static_cast<int>(DataRoles::CustomData)] = "customData";
return res;
}
- Return corresponding data in overloaded
data()
member function.
e.g.:
QVariant TestList::data(const QModelIndex & index, int role) const
{
QVariant result = QVariant();
if ( index.isValid() == true ) {
EntityPtr entityPtr = entities_.at(index.row());
switch (role) {
case DataRoles::Name:
result = entityPtr->name();
break;
case DataRoles::Description:
result = entityPtr->description();
break;
case DataRoles::CustomData:
result = entityPtr->customData();
break;
}
}
return result;
}
Than, after registering your model instance in QML's context, you can access entity fields in the ListView delegate by name, defined in rolesNames(), e.g.:
ListView {
model: yourModelInstance
...
delegate: Item {
...
Text {
text: name // access DataRoles::Name
}
...
}
}
IMO, this implementation is all good, but when it comes to changing properties from C++ side, you should call dataChaged() signal of QAbstractListView
on each change of entity's property.
And I want to be able to automatically inform view about changes, e.g. when I'm calling entity->setName()
.
What I have in mind is to register only one data role, e.g. "object" which will return whole QObject, which will have Q_PROPERTY
s and then access it from qml like:
Text {
text: object.name //access `name` Q_PROPERTY of the object
}
This way, if setters will emit right signals, registered in Q_PROPERTY declaration, QML side will be automatically informed about changes on setting new value.
So the question is, if this is a good way for achieving my goal, because, as I've said above, it feels a bit unnatural, and Qt library is a good in indicating that you are doing something wrong by making it hard (unnatural) to do wrong things.
EDIT:
As was requested in the comments, I've made a small Qt example app showing what I want to achieve.
QObjectList
and exposes a certain property or properties of the objects as individual cells. You wouldn't be passing around anyQObject
s though -QVariant
was never meant to work with them. It's a value class, andQObject
is not a value class. – Kuba hasn't forgotten MonicaQObjectList
is just a typedef forQList<QObject*>
, isn't it? I would face the same problems for notifying about props being changed. The whole aim is to release developer from obligation to calldataChanged
signal each time he wants to change some property OR notify model from object setter function somehow. – rshtxxxChanged()
signal from each entity to the model'sdataChaged()
signal on adding entity to the model. This doesn't sound like a fun thing to maintain + imo, will lead to stronger coupling, which is rarely good. Another option tho, is to use setData() of QAbstractListView each time user wants to change data. This would lead to possibility for changing data from QML + good amount of other encapsulation problems. – rshtdataChanged
: the sender won't provide the args. – Kuba hasn't forgotten Monica