0

I am having problems exposing my C++ data model to a ListView in QML. QML can't find the properties of each row.

I have this class (QT 5.7):

class Identity : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged);
    Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged);
    ...
}

And I have a model class that stores Identity objects:

class Identities: public QAbstractItemModel {
    Q_OBJECT

private:
    QMap<QString,Identity*>          identities_map;
    ...
}

I'm also registering it in main.cpp:

qmlRegisterType<Identities>("project_identities",1,0,"Identities");

My main.qml has these definitions:

Identities {
    id: identidades
}
ListView {
    anchors.fill: parent
    model: identidades
    delegate: Row {
        Text { text: "Email: " + model.email}
    }
}

Now the problem is, when I display the list of emails and passwords, this is the output:

Email: undefined

What I am missing ? When I debug my code with the debugger, the only method that is being executed is Identities::rowCount(). The Identities::index() is not executed, nor Identities::data().

Where could the problem be?

dtech
  • 47,916
  • 17
  • 112
  • 190
Nulik
  • 6,748
  • 10
  • 60
  • 129

2 Answers2

1

Providing the roles in the model are properly implemented, all you need to do is:

Text { text: "Email: " + email}.

If that doesn't work, then definitely you have a problem with your model implementation.

As GrecKo noted in the comments, the format model.role is actually possible, but not really necessary unless you have name conflicts.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • if I do it like you say, I get " ReferenceError: email is not defined" error. I have tried it already – Nulik Nov 13 '16 at 23:52
  • Emphasis on the "providing the roles in the model are properly implemented" part. – dtech Nov 13 '16 at 23:54
  • I think the problem is with the delegate. The delegate is not calling data() nor index() method , so nothing is ever executed at the model class. I have breakpoints everywhere in it but there is no calls – Nulik Nov 14 '16 at 03:03
  • There is no particular requirement for the delegate, if the model works, the delegate will work, it is all automatic. The problem is **most likely** with your model. – dtech Nov 14 '16 at 03:31
  • If your model items are `QObject` derived, you can try using this generic model: http://stackoverflow.com/questions/35160909/how-to-create-a-generic-object-model-for-use-in-qml/35161903#35161903 – dtech Nov 14 '16 at 03:33
  • I found the problem. I didn't implement the roleNames() method in my subclass of the model. I didn't understand that the model has to use one role for each (QML) property. Not very clean solution imho, kind of a hack, but anyway... Now that I implemented roles everything works fine. Thanks! – Nulik Nov 14 '16 at 04:24
  • 2
    "That's not how you use roles, the item role is not a property of the list object, you don't do model.role you just do role." That's not true at all. You can use `model.role` in a delegate when `role` is ambiguous (and I do believe it's a better convention) : http://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html#models – GrecKo Nov 14 '16 at 09:11
  • @GrecKo - fair enough, I didn't expect that, because what you call "a better conventions" seems like a really half-baked solution - it may fix ambiguities between the model item and the delegate, but it introduces ambiguities between the model item and the model. They should have used something more specific, for example `modelData.role`. – dtech Nov 14 '16 at 13:22
  • modelData could also be ambigous. But the roles come from an possible external source, which you can't rename, while the internal sources (e.g. the model) can be accessed by the id, a property alias e.t.c. there are multiple ways to resolve name conflicts for the internaly defined names. – derM - not here for BOT dreams Nov 14 '16 at 15:42
  • @derM - not necessarily - it trivial scenarios maybe, but the list view model can come from everywhere, and may not necessarily be referable from the delegate source. My personal convention is to use an `m_` prefix for roles, that deals with ambiguity and is less verbose. – dtech Nov 14 '16 at 15:51
  • @ddriver: How? Let's take the `ListView`-Case. I can't see any way where I could not reference the model at the latest by `listViewID.model`. But if I don't like that, I might as well define a helper-property in the listView: `property alias theRealModelThingy: listViewID.model` which then might be used thereafter... But I would resort to use the fully qualified `listViewID.model` as proposed in the documentation. – derM - not here for BOT dreams Nov 14 '16 at 16:14
  • Another way to access the Model would be `delegateID.ListView.view.model` but I don't like that. It has too much magic to it. http://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html#accessing-views-and-models-from-delegates – derM - not here for BOT dreams Nov 14 '16 at 16:23
  • @GrecKo I agree, always prefer better readability over saving to type a few characters. `model.propertyName` is way more explicitl than just `propertyName` as the latter will always require a reader to first check for local properties before concluding it came from the model. – Kevin Krammer Nov 19 '16 at 11:40
0

Your model is either not mapping the role name correctly to the role value, i.e. missing or wrong implementation of QAbstractItemModel::roleNames() or your data() method is not correctly processing the role values.

Since your data object's seem to be QObjects you could have a single model role that returns the object of a given index and access that object's properties instead.

Kevin Krammer
  • 5,159
  • 2
  • 9
  • 22