2

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

rsht
  • 1,522
  • 12
  • 27
  • 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 `QObject`s 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 Mar 04 '16 at 15:11
  • `QObjectList` is just a typedef for `QList`, 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 Mar 04 '16 at 15:57
  • 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 Mar 04 '16 at 17:02
  • 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 Mar 04 '16 at 20:25
  • 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 Mar 04 '16 at 20:26
  • @KubaOber thank you for helping and sorry for the delay. I've update my post with a link to the repo where you can clone simple Qt example project basically showing what I want to achieve. – rsht Mar 07 '16 at 18:27
  • Long story short: I have some devices, which can be added to the system from different sources. Once they are added from either source, they can't be deleted, they are just updated with an info about their availability in separate sources. You can find code from Device entity, model and simple user in the device.h/device.cpp files in in my projects. – rsht Mar 07 '16 at 18:34
  • Qml part is showing what devices are available from which sources. You can add new dev with a text input in the bottom (name of the dev), setting source with a checkbox (source1 or source2) and pressing "connect" button. Gosh, this is where you want to continue this discussion on some kind of forum, I guess :) – rsht Mar 07 '16 at 18:37
  • Your two comments above should be integrated into the narrative in the question, and then deleted :) – Kuba hasn't forgotten Monica Mar 08 '16 at 13:25
  • https://stackoverflow.com/questions/35160909/how-to-create-a-generic-object-model-for-use-in-qml – dtech Feb 04 '18 at 00:20

1 Answers1

0

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.

vkensou
  • 51
  • 4