Exposing C++ model property of type QList<QObject*> to QML



  • Hello, sorry if this has been covered but I did try to search and could not find a solution.

    I am trying to use QtQuick to build user interface and work with C++ model.

    So far the model is as simple as this.

    @class Axis : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QString name READ name NOTIFY nameChanged)
    public:
    // ...
    QString name() const;
    // ...
    };

    class ApplicationData : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QList<QObject*> axes READ axes NOTIFY axesChanged)
    public:
    // ...
    QList<QObject*> axes() const;
    // ...
    private:
    QList<QObject*> _axes;
    };

    ApplicationData::ApplicationData(QObject *parent) :
    QObject(parent)
    {
    _axes.append(new Axis("Axis 1", this));
    _axes.append(new Axis("Axis 2", this));
    _axes.append(new Axis("Axis 3", this));
    _axes.append(new Axis("Axis 4", this));
    _axes.append(new Axis("Axis 5", this));
    }@

    Now I am publishing the model like this:

    @ ApplicationData data;
    engine.rootContext()->setContextProperty("appData", &data);
    @

    In QML I want to display my axes list like this:
    @ListView {
    model: appData.axes
    delegate: Text { text: name }
    }@

    But it is not displaying. When I run the code under QtCreator, the following messages are printed.
    @
    file:///.../app/Setup.qml:23: ReferenceError: name is not defined
    file:///.../app/Setup.qml:23: ReferenceError: name is not defined
    @

    The number of messages equals to the number of objects in the object list, so it must be iterating correct object.
    However for some reason I does not seem to be able to access individual elements from the list.

    Also the following would work perfectly and display correct string:
    @Text {
    text: appData.axes[2].name
    }@

    Also the ListView will display the list as expected, if I publish it like this:
    @
    // C++ code
    engine.rootContext()->setContextProperty("axes", QVariant::fromValue(data.axes()));

    // QML code
    ListView {
    model: axes
    delegate: Text { text: name }
    }@

    Could anybody suggest what am I missing or how I can workaround?

    Thanks



  • According to the documentation in "Exposing Attributes of C++ Types to QML":http://doc-snapshot.qt-project.org/qt5-stable/qtqml/qtqml-cppintegration-exposecppattributes.html#properties-with-object-list-types QQmlListProperty<> is recommended for exposing object-list type properties.



  • Roland_R, thank you for your reply. I missed that article. Indeed, it started working after I changed the property to be of type QQmlListProperty.



  • Hi,

    I am building Sailfish application and have problems when trying to expose QList<QObject*> to QML-side.

    Here is something that works:

    main.cpp:

    @QList<QObject*> datalist;
    SeriesData* serie = new SeriesData(0, "seriesid", "language",
    "seriesName", "aliasNames",
    "banner", "overview", "firstAired",
    "imdb_id", "zap2it_id", "network");

    datalist.append(serie);
    
    view->rootContext()->setContextProperty("myModel",
    

    QVariant::fromValue(datalist));@

    QML:

    @SilicaListView {
    id: listView
    anchors.top: seriesSearch.bottom
    height: 800
    width: 540
    model: myModel

        header: PageHeader {
            title: "SailSeries"
        }
    
        delegate: ListItem {
            id: listItem
            contentHeight: Theme.itemSizeSmall
            contentWidth: listView.width
            Column {
                x: Theme.paddingLarge
                Label {
                    id: seriesName
                    text: SeriesName.length === 0 ? text = "toimii" : text = SeriesName
                    color: listItem.highlighted ? Theme.highlightColor : Theme.primaryColor
                }
                Label {
                    id: overview
                    text: Overview
                    font.pixelSize: Theme.fontSizeSmall
                    color: listItem.highlighted ? Theme.highlightColor : Theme.secondaryColor
                }
            }
        }@
    

    However, when I am trying to do exactly or almost the same thing by using a Q_INVOKABLE function from C++ class, the program crashes. The line of code that is responsible for the crash is :

    @myContext->setContextProperty("myModel",QVariant::fromValue(mySeriesListModel));@

    myContext is QQmlContext.

    I tried to put the working code to a Q_INVOKABLE function but the program crashed again when I called setContextProperty().

    What am I doing wrong here? I hope I gave enough information about the problem. This is a major problem for me because I have 2 apps waiting for this to solve. :D



  • I suspect the issue with your context. Can you check you myContext or mySeriesListModel. Hope they are not null values. Also did you try this QMListProperty to expose list of QObjects ? May be you can put your complete code in dropbox. I can look at suggest you the issue.



  • Hi, thanks for the reply. I checked myContext and I think it is fine, I also think that I have to look at the QMListProperty a bit more and I will ask more when I understand more about it :)



  • @joone
    QList is not a QObject based class.
    So that's why it is not working.
    Either you create an access class to your QList Object like in this link:
    http://qt-project.org/wiki/How_to_expose_lists_to_QML

    Or you set the model from C++ via
    QObject::findChild() and
    QObject::setProperty("model", ....);

    You have to use one of the model classes like:

    • QAbstractItemModel
    • QAbstractListModel
    • QStringListModel

    It depends on your code which solution would be the best for you.



  • Hi,

    thanks for reply!

    I followed the documentation at this "link":http://qt-project.org/doc/qt-5/qqmllistproperty.html and got it to compile, now the only problem is that I am not sure how I am supposed to update the model and let the QML-side know about changes in the model.

    I have the NOTIFY signal of the Q_PROPERTY in my code; should it work alone or am I supposed to update the model via setContextProperty() as I was trying to do earlier?

    Also I would like to understand better which is better; register the class with qmlRegisterType and then set up the model in QML or use setContextProperty() in main.cpp ? Can I use them both at the same time? I am still a bit confused with these.



    1. Question
      Some where in your C++ model you have to emit the signal function which is connected to your NOTIFY declaration.

    2.Question
    http://qt-project.org/forums/viewthread/13210



  • Hi,

    I now have a new problem or I am a bit confused. This relates to the above posts so I thought I could ask this here.

    I want to make/have started to make an TV-Guide view for my Sailfish/QT5 -application. I have SlideShowView and I use VisualItemModel as model for it. Then I am creating my own Channel-Items to have own page for each channel.

    In the Channel.qml I have SilicaListView and I am trying to use my own C++ and QQmlListProperty-based model for it. So the first question is that is it okay to make a new Model for each Channel-Page's listview?

    The main problem for me is to understand where and how should I initialize the model; in the qml file or in C++? Also I would like to know is it okay to have QList<MyListModel*> and then return the right one for the right tv-channel?

    Here is some code to help to understand my issue:

    @ SlideshowView {
    id: channelView
    width: tvguidepage.width
    height: tvguidepage.height
    itemWidth: width

        model: VisualItemModel {
            id: channels
            Channel {id: tv1 }
            Channel {id: tv2 }
            Channel {id: mtv3 }
        }
    }
    
    function initialize() {
    
        tv1.initialize("Yle TV1");
        tv2.initialize("Yle TV2");
        mtv3.initialize("MTV3");
    
    }@
    

    @property ProgramListModel programlist

    function initialize(channel) {
        // I tried to set the right channel
        dataModel.setDesiredChannel(channel);
        // and then just return it with Q_PROPERTY(READ) 
        listview.model = dataModel.programListModel
        console.log("ProgramListModel" + listview.model) // it isn't null
    }
    
    SilicaListView {
        id: listview
    
        header: Component {
            PageHeader {
                id: pageheader
                title: "channel"
            }
        }
    
        anchors.fill: parent
    
        delegate: ListItem {
    
            width: parent.width
    
            Column {
    
                Label {
                    // role's name from C++
                    text: programName
                }
    
                Label {
                    // role's name from C++
                    text: time
    
                }
            }
        }@


  • Hi Joonne,

    lately I discussed exact this topic with my friends.
    Our conclusion is this. Use one model for all data.
    With a QAbstractProxyModel you can decide which data should be displayed on which page. Yoou can also add a QAbstractSortModel as well.

    Do the model in C++. Use either QAbstractListModel or QAbstractItemModel.
    Create a QAbstractProxyModel as well.

    Register both modeles in your main.cpp with qmlRegisterType

    Here is a small example:

    @Rectangle {
    property int entriesPerPage: 9
    width: 360
    height: 360

    MyModel { id: mainModel }
    
    ListView {
        id: listView
        width: parent.width
        height:parent.height
        anchors.centerIn: parent
        objectName: "listView"
    
        snapMode: GridView.SnapToRow
        orientation: Qt.Horizontal
    
        model: mainModel.rows/entriesPerPage // Check if this works
    
        delegate:
        Component {
            Rectangle {
                id: listItem
                width: 360
                height: 360
                border.color: "blue"
                border.width: 2
    
                GridDelegate {
                    width: 300
                    height: 300
    
                    model: MyProxyModel {
                        mySource: mainModel
                        itemsPerPage: entriesPerPage
                        pageNumber: index
                    }
                }
            }
        }
    }
    

    }@



  • Thanks for reply!

    I am maybe a little more confused now. :D

    I would like to know if you meant that I should somehow place all the tv-programs as a list to QAbstractListModel and then select the correct ones with QModelIndex with the mapFromSourceFunction() ?

    I just learned how to use the QQmlListProperty model and this seems very complicated.



  • Hi Joonne

    yes exactly. You have to implement in your ProxyModel mapFromSource(), mapToSource(), index(), columnCount(), rowCount().

    you mentioned that you are programming a TV-Guide with a SlideShowView.
    So when you have got e.g. 200channels and you like to display 10channels/page then you will have 20 pages to navigate.

    The QAbstractListModel will hold all your channel unsorted (raw).
    Now you can add an extra QAbstractSortModel where you can add your sorting rule e.g. channel number sorting, channel name sorting, favorite sorting etc.
    You can also add a QAbstractProxyModel for filtering.

    With the QAbstractProxyModel you can define how many channel per pages shall be displayed. In my example you will have for each page a ProxyModel.

    I hope my explanation clarifies it better.



  • Okay, thanks again!

    I think I understood what you meant. However my use-case is going to be a little bit different from what you explained.

    One page of the SlideShowView should have all the programs of the channel, so when swiping left/right, you have a new channel and the programs for that channel. I don't know yet how I can achieve this kind of model-structure. Please tell me if I am thinking this all wrong. :)



  • Any ideas for this kind of situation?

    If I make a QAbstractListModel that contains all the episodes of all channels, could I then filter the right ones for the correct page? Could I access the roles of the program-class with the sortmodel ?

    Thanks in advance :)



  • Any ideas for this kind of situation?

    If I make a QAbstractListModel that contains all the episodes of all channels, could I then filter the right ones for the correct page? Could I access the roles of the program-class with the sortmodel ?

    Thanks in advance :)



  • Hi joonne

    you have two possibilities:
    1.) Model in model. One for the channel and one model for the channel program
    With this solution you can easily set the program model to the program list view.

    2.) Create a TreeModel
    http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html

    Both are possible.



  • Hi joonne

    you have two possibilities:
    1.) Model in model. One for the channel and one model for the channel program
    With this solution you can easily set the program model to the program list view.

    2.) Create a TreeModel
    http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html

    Both are possible.



  • I know this is an old thread, but I had a similar problem to that of the original poster (stas2)

    I was able to have the QML read the QList properties correctly after using the following syntax in the QML view:

    @ListView {
    model: appData.axes
    delegate: Text { text: model.modelData.name }
    }@



  • I know this is an old thread, but I had a similar problem to that of the original poster (stas2)

    I was able to have the QML read the QList properties correctly after using the following syntax in the QML view:

    @ListView {
    model: appData.axes
    delegate: Text { text: model.modelData.name }
    }@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.