1
votes

So I have this idea of using Q_PROPERTYs 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:

  1. Inherit QAbstractListModel (or others) for creating your custom model.
  2. Overload rowCount()
  3. 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;
}
  1. 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_PROPERTYs 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.

git repo - https://github.com/shtemberko/SO_question_temp

1
You can easily create a model that wraps a QObjectList and exposes a certain property or properties of the objects as individual cells. You wouldn't be passing around any QObjects though - QVariant was never meant to work with them. It's a value class, and QObject is not a value class.Kuba hasn't forgotten Monica
QObjectList is just a typedef for QList<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 call dataChanged signal each time he wants to change some property OR notify model from object setter function somehow.rsht
I agree with your comments above. Nevertheless, if QObject's properties are not used directly, I see only one possible implementation - connect each xxxChanged() signal from each entity to the model's dataChaged() 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.rsht
Signals and slots are under full programmatic control. You can enumerate them and connect them automatically. As long as you have a list of objects that send these signals, and an object-to-row/column or signal-to-row/column assignment stored somewhere, this can be done without writing any per signal code. In fact, you shouldn't be writing any per-signal code for it anyway. Please describe your architecture in more detail, perhaps providing a code sample to illustrate what you are trying to achieve. Anyway, you can't really connect anything to dataChanged: the sender won't provide the args.Kuba hasn't forgotten Monica
Again: All of what you described so far is achievable with a generic piece of code that enumerates the necessary signals or properties and makes the connections automatically. There isn't enough implementation given in the question to propose a solution at this point. What is an "entity"? Help us help you.Kuba hasn't forgotten Monica

1 Answers

0
votes

I think you need a propertyChanged signal in Entity for each properties. And then link propertyChanged to dataChanged signal. When you setName or setPosition, emit propertyChanged signal.