Using C++ model in QML



  • I have a ListView in my QML file with a model from C++. The model have a QString, a bool and a QList.

    The model works perfect when I don't use the QList:

        QList<QObject*> dataList;
        DataObject *ob = new DataObject("main", false);
        dataList.append(ob);
        dataList.append(new DataObject("Item 2", false));
        dataList.append(new DataObject("Item 3", true));
        dataList.append(new DataObject("Item 4", true));
    
        QQmlContext *ctxt = engine.rootContext();
        ctxt->setContextProperty("checkModel", QVariant::fromValue(dataList));
    

    But the DataObject class contains a QList that contains a list of DataObject classes. Here is where I have problems, as I can't make my project compile.

    I have tried declaring my instance variable as a list of QVariant pointers:

    private:
        QString m_group;
        bool m_checked;
        QList<QVariant*> m_elements;
    

    But my addElement method will not compile, complaining about:
    no matching member function for call to 'append'

    void DataObject::addElement(DataObject *ob)
    {
        m_elements.append(QVariant::fromValue(ob));
    }
    

    What am I doing wrong here?


  • Moderators

    @imyrvold
    So hwo about QList<DataObject*> dataList ? and then the rest as usual i.e appending DataObject directly instead of QVariant



  • @p3c0
    The first thing I tried, was as you suggested, but this doesn't quite work, because I want dataList to function as a ListView model, like this:

    Item {
        width: 200
        height: childView.contentHeight
        visible: mainRow.expanded
    
        ListView {
            id: childView
            anchors.fill: parent
            model: elements
            delegate: groupsDelegate
            focus: true
        }
    }
    
        DataObject *ob = new DataObject("main", false);
        DataObject *ob1 = new DataObject("hr", true);
        DataObject *ob2 = new DataObject("ht", true);
        ob->addElement(ob1);
        ob->addElement(ob2);
        dataList.append(ob);
        dataList.append(new DataObject("Item 2", false));
        dataList.append(new DataObject("Item 3", true));
        dataList.append(new DataObject("Item 4", true));
    
        QQmlContext *ctxt = engine.rootContext();
        ctxt->setContextProperty("checkModel", QVariant::fromValue(dataList));
    

    My addElement is:

    void DataObject::addElement(DataObject *ob)
    {
        m_elements.append(ob);
    }
    

    I get the two elements in the ListView, but QML doesn't recognize them to be a ListModel, because I get a reference error in the log and they shows up without the group name

    qrc:/TreeView.qml:40: ReferenceError: elements is not defined
    qrc:/TreeView.qml:61: ReferenceError: group is not defined
    qrc:/TreeView.qml:73: ReferenceError: elements is not defined
    qrc:/TreeView.qml:40: ReferenceError: elements is not defined
    qrc:/TreeView.qml:61: ReferenceError: group is not defined
    qrc:/TreeView.qml:73: ReferenceError: elements is not defined
    qml: [object Object]
    qml: [DataObject(0x7faceb2958b0),DataObject(0x7faceb295970)]

    and the two console.log lines shows the difference between the checkModel and the elements list (the last with the list of DataObject's).

    The result is this:
    IncompleteCheckBoxes



  • Here is an example of how I want the ListModel to be:

            ListModel {
                id:listModel
                ListElement {
                    group: "main"
                    elements: [
                        ListElement {
                            group: "hr"
                            elements: [
                                ListElement {
                                    group: "mainarmA"
                                    elements: []
                                },
                                ListElement {
                                    group: "mainarmC"
                                    elements: []
                                },
                                ListElement {
                                    group: "mainarmE"
                                    elements: []
                                }
                            ]
                        },
                        ListElement {
                            group: "ht"
                            elements: [
                                ListElement {
                                    group: "mainarmD"
                                    elements: []
                                }
                            ]
                        }
                    ]
                }
                ListElement {
                    group: "aux"
                    elements: [
                        ListElement {
                            group: "hr"
                            elements: [
                                ListElement {
                                    group: "mainarmB"
                                    elements: []
                                }
                            ]
                        }
                    ]
                }
            }
    

    Then it should look like this:
    IMG


  • Moderators

    @imyrvold It seems you want to implement TreeView. why don't you use it directly ? Qt 5.5 now has TreeView.



  • @p3c0
    I didn't know that. I will investigate it, certainly. Thank you for the tip!



  • To wrap this up, I have concluded that setting the QQmlContext property doesn't work in my case. I found another way to do it, that works perfectly, with the same DataObject, and that is to use QMetaObject to invokeMethod in my ListModel with the DataObject as parameter, and append the DataObject in the invoked function.
    In the following main.cpp listing which shows this, the commented out lines are the ones that for some reason didn't work:

    #include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlComponent>
    //#include <QQmlContext>
    #include <qqml.h>
    #include <QQuickItem>
    #include <QQuickView>
    #include "dataobject.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
     //    QList<QObject*> dataList;
    
        DataObject *ob = new DataObject("main", false);
        DataObject *ob1 = new DataObject("hr", false);
        DataObject *ob2 = new DataObject("ht", true);
        DataObject *ob11 = new DataObject("mainarmA", false);
        DataObject *ob12 = new DataObject("mainarmC", false);
        DataObject *ob13 = new DataObject("mainarmE", true);
        DataObject *ob21 = new DataObject("mainarmD", true);
        ob1->addElement(ob11);
        ob1->addElement(ob12);
        ob1->addElement(ob13);
        ob2->addElement(ob21);
        ob->addElement(ob1);
        ob->addElement(ob2);
    
        QVariant v;
        v = QVariant::fromValue(ob);
    
    //    dataList.append(ob);
    
    //    QQmlContext *ctxt = engine.rootContext();
    //    ctxt->setContextProperty("checkModel", QVariant::fromValue(dataList));
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        QMetaObject::invokeMethod((QObject *)engine.rootObjects().first()->findChild<QObject *>("checklistModel"),  "appendElement", Q_ARG(QVariant, v));
    
        return app.exec();
    }
    

    and the ListModel with the invoked function:

            ListModel {
                id:checklistModel
                objectName: "checklistModel"
                function appendElement(ob) {
                    checklistModel.append(ob)
                }
            }
    

  • Moderators

    @imyrvold Glad that you found the solution :) But to be future ready use TreeView as the view and QAbstractItemModel as the model.


Log in to reply
 

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