Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML List is not updated by QAbstractListModel

QML List is not updated by QAbstractListModel

Scheduled Pinned Locked Moved Solved QML and Qt Quick
7 Posts 3 Posters 1.7k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Krulle
    wrote on last edited by
    #1

    Hello Guys,
    I am a bit frustrated because I don't see the problem in this code.
    Currently there is a problem with the ListView and the visible of the elements in the list.
    I am following this tutorial and trying to get a c++ model to run in QML.

    The Problem:
    With the C++ model, the ListView is not updated when a new element is added. However, if I use the QML model, it works wonderfully.
    If I put the cpp model into a minimal example (as seen at the end), it works.
    Does anyone see the problem? I suspect it must be in the QML part.

    Thanks a lot.

    main.cpp

    #include "app_environment.h"
    #include "models/functionitem.h"
    #include "models/functionmodel.h"
    #include "membermodel.h"
    
    #include "memberlist.h"
    #include "membermodel.h"
    
    Q_IMPORT_QML_PLUGIN(UtilsPlugin)
    Q_IMPORT_QML_PLUGIN(BackendPlugin)
    
    int main(int argc, char *argv[])
    {
        set_qt_environment();
    
        QGuiApplication app(argc, argv);
    
        qmlRegisterUncreatableType<FunctionItem>("Models", 1, 0, "FunctionItem", QStringLiteral("Only Interface. Cannot create in QML"));
        qmlRegisterUncreatableType<FunctionModel>("Models", 1, 0, "FunctionModel", QStringLiteral("Only Interface. Cannot create in QML"));
    
        qmlRegisterUncreatableType<MemberList>("Models", 1, 0, "MemberList", QStringLiteral("Only Interface. Cannot create in QML"));
        qmlRegisterType<MemberModel>("Models", 1, 0, "MemberModel");
    
        MemberList memberList;
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty(QStringLiteral("memberList"), &memberList);
    
        const QUrl url(u"qrc:/Classcreator/main.qml"_qs);
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
    
        engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
        engine.addImportPath(":/");
    
        engine.load(url);
    
        if (engine.rootObjects().isEmpty()) {
            return -1;
        }
    
        return app.exec();
    }
    

    MemberList and Item

    class MemberItem
    {
    public:
        explicit MemberItem();
        MemberItem(const QString name, const QString type, const bool getter, const bool setter, const int protectionLevel);
        ~MemberItem() { }
        MemberItem &operator=(const MemberItem &other);
    
        QString name() const;
        void setName(const QString &newName);
    
        QString type() const;
        void setType(const QString &newType);
    
        bool getter() const;
        void setGetter(bool newGetter);
    
        bool setter() const;
        void setSetter(bool newSetter);
    
        int protectionLevel() const;
        void setProtectionLevel(int newProtectionLevel);
    
    private:
        QString m_name;
        QString m_type;
        bool m_getter;
        bool m_setter;
        int m_protectionLevel;
    };
    
    class MemberList : public QObject
    {
        Q_OBJECT
    public:
        explicit MemberList(QObject *parent = nullptr);
    
        QVector<MemberItem> items() const;
    
        bool setItemAt(int index, const MemberItem &item);
    
    signals:
        void preItemAppended();
        void postItemAppended();
    
        void preItemRemoved(int index);
        void postItemRemoved();
    
    public slots:
        void appendItem(const QString &name, const QString &type, const bool getter, const bool setter, const int protectionLevel);
        void removeItemAt(int index);
        int count();
    
    private:
        QVector<MemberItem> m_items;
    
    };
    

    Membermodel

    #ifndef MEMBERMODEL_H
    #define MEMBERMODEL_H
    
    #include <QObject>
    #include "qabstractitemmodel.h"
    
    Q_MOC_INCLUDE("MemberList.h")
    
    class MemberList;
    
    class MemberModel : public QAbstractListModel
    {
        Q_OBJECT
        Q_PROPERTY(MemberList *list READ list WRITE setList)
    
    public:
        explicit MemberModel(QObject *parent = nullptr);
    
        enum {
            NameRole = Qt::UserRole,
            TypeRole,
            GetterRole,
            SetterRole,
            ProtectionLevelRole
        };
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
        Qt::ItemFlags flags(const QModelIndex& index) const override;
    
        virtual QHash<int,QByteArray> roleNames() const override {
                return { { NameRole, "name" },
                    { TypeRole, "type" },
                    { GetterRole, "getter" },
                    { SetterRole, "setter" },
                    { ProtectionLevelRole, "protectionLevel" }
                };
        }
    
        MemberList *list() const;
        void setList(MemberList *newList);
    
    private:
        MemberList *m_list = nullptr;
    };
    
    #endif // MEMBERMODEL_H
    
    

    App.qml
    Screen01 is a UI-file

    import QtCore
    import QtQuick
    import QtQuick.Window
    import QtQuick.Controls
    import QtQuick.Dialogs
    import Qt.labs.settings
    import "Delegate"
    import "CalcSizeOfElements.js" as Calc
    import Backend
    import Utils
    import Models
    
    Window {
        id: root
        width: Constants.width
        height: Constants.height
        minimumWidth: 1000
        minimumHeight: 400
    
        BuildClass {
            id: classcreator
        }
    
    // this works properly!
    //    ListModel {
    //        id: listMemberModel
    //    }
    
    // this doesn't work
        MemberModel {
            id: memberModel
            list: memberList
        }
    
        ListModel {
            id: listFunctionModel
        }
    
        Component.onCompleted: {
            mainScreen.pathInput.path = Constants.cleanPath(settings.savePath)
        }
    
        Component.onDestruction: {
            settings.savePath = mainScreen.pathInput.path
        }
    
        Settings {
            id: settings
            property alias savePath: fileDialog.currentFolder
        }
    
        Screen01 {
            id: mainScreen
            width: root.width
            height: root.height
    
            butAddMember.onClicked: {
                memberList.appendItem("m_",
                                  mainScreen.textFieldVarTypeText,
                                  mainScreen.checkBoxMainGetter.checked,
                                  mainScreen.checkBoxMainSetter.checked,
                                  0)
    
                 mainScreen.labelMemberText = "Member (%1)".arg(memberList.count())
            }
    
            listMember.model: memberModel
            listMember.delegate: MemberListDelegate {
                onDeleteItem: function(index) {
                    listMemberModel.remove(index)
                    mainScreen.labelMemberText = "Member (%1)".arg(models.memberModel.count())
                }
            }
    
            butAddFunction.onClicked: {
                listFunctionModel.append({
                    "retType": mainScreen.textFieldReturnTypeText,
                    "name": "",
                    "protectionLevel": mainScreen.comboMainProtectionLevel.currentIndex,
                    "hidingLevel": mainScreen.comboMainHidingLevel.currentIndex
                })
    
                mainScreen.labelFunctionText = Calc.setListView(listFunction, "Function")
            }
    
            listFunction.model: listFunctionModel
            listFunction.delegate: FunctionListDelegate {
                onDeleteItem: function(index) {
                    listFunctionModel.remove(index)
                    mainScreen.labelFunctionText = Calc.setListView(mainScreen.listFunction, "Function")
                }
            }
        }
    }
    
    

    MemberListDelegate.qml

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 6.3
    import Utils
    import Models
    
    Item {
        id: item1
        width: parent.width == null ? 400 : parent.width
        height: 40
        property alias buttonDeleteElement: buttonDeleteElement
    
        signal deleteItem(int index)
    
        Component.onCompleted: console.log("completed elem... " + name)
        Component.onDestruction: console.log("delete elem... " + name)
    
        RowLayout {
            id: rowLayout
            anchors.fill: parent
    
            TextField {
                id: textField1
                Layout.preferredWidth: 50
                Layout.preferredHeight: 28
                placeholderText: qsTr("Type")
                text: type
            }
    
            TextField {
                id: textField
                Layout.rightMargin: 50
                Layout.fillWidth: true
                Layout.preferredHeight: 28
                placeholderText: qsTr("Membername")
                text: name
                Layout.minimumWidth: 120
            }
    
            ComboBox {
                id: comboProtectionLevel
                Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
                Layout.preferredWidth: 120
                Layout.preferredHeight: 28
                model: Constants.protectionLevelModel
                currentIndex: protectionLevel
                textRole: "protectionLevel"
            }
    
            CheckBox {
                id: checkGetter
                text: qsTr("getter")
                Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
                checked: getter
            }
    
            CheckBox {
                id: checkSetter
                text: qsTr("setter")
                Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
                checked: setter
            }
    
            Button {
                id: buttonDeleteElement
                Layout.preferredHeight: 28
                Layout.preferredWidth: 28
                icon.source: Constants.getIconFile("action", "ic_delete")
                onClicked: item1.deleteItem(index)
    
            }
        }
    }
    
    

    Minimal example:
    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml/qqmlextensionplugin.h>
    #include <QQmlContext>
    
    #include "app_environment.h"
    #include "memberlist.h"
    #include "membermodel.h"
    
    int main(int argc, char *argv[])
    {
        set_qt_environment();
    
        QGuiApplication app(argc, argv);
    
        qmlRegisterUncreatableType<MemberList>("Models", 1, 0, "MemberList", QStringLiteral("Only Interface. Cannot create in QML"));
        qmlRegisterType<MemberModel>("Models", 1, 0, "MemberModel");
    
        MemberList memberList;
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty(QStringLiteral("memberList"), &memberList);
    
        const QUrl url(u"qrc:/Classcreator/main.qml"_qs);
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
    
        engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
        engine.addImportPath(":/");
    
        engine.load(url);
    
        if (engine.rootObjects().isEmpty()) {
            return -1;
        }
    
        return app.exec();
    }
    

    main.qml

    import QtQuick
    import QtQuick.Controls 6.3
    import Models
    
    Window {
        id: root
        height: 400
        visible: true
        color: "#00ffffff"
        minimumWidth: 1000
        minimumHeight: 400
    
        MemberModel {
            id: memberModel
            list: memberList
        }
    
        ListView {
            id: listView
            width: 400
            height: 200
            anchors.verticalCenter: parent.verticalCenter
            z: 3
            anchors.horizontalCenter: parent.horizontalCenter
            model: memberModel
            delegate: Item {
                x: 5
                width: 80
                height: 40
                Row {
                    id: row1
                    Rectangle {
                        width: 40
                        height: 40
                        color: colorCode
                    }
    
                    Text {
                        text: name
                        anchors.verticalCenter: parent.verticalCenter
                        font.bold: true
                    }
                    spacing: 10
                }
            }
        }
    
        Button {
            id: button
            y: 371
            text: qsTr("Button")
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 8
            anchors.leftMargin: 8
            onClicked: {
                memberList.appendItem("m_",
                                   "mainScreen.textFieldVarTypeText",
                                   true,
                                   true,
                                   0)
            }
        }
    
    }
    
    1 Reply Last reply
    0
    • jeremy_kJ Offline
      jeremy_kJ Offline
      jeremy_k
      wrote on last edited by
      #2

      That's a lot of code for a forum post, but I don't see the model implementation, and in particular anything calling QAbstractItemModel::beginInsertRows() and QAbstractItemModel::endInsertRows()

      Using the model tester is worth a try.

      Asking a question about code? http://eel.is/iso-c++/testcase/

      K 1 Reply Last reply
      0
      • jeremy_kJ jeremy_k

        That's a lot of code for a forum post, but I don't see the model implementation, and in particular anything calling QAbstractItemModel::beginInsertRows() and QAbstractItemModel::endInsertRows()

        Using the model tester is worth a try.

        K Offline
        K Offline
        Krulle
        wrote on last edited by
        #3

        @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.
        But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

        jeremy_kJ 1 Reply Last reply
        0
        • K Krulle

          @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.
          But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

          jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by
          #4

          @Krulle said in QML List is not updated by QAbstractListModel:

          @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.

          I'm not clear on whether list is referring to the model (which has no on-screen dimensions), or the view (which has defined dimensions in the minimal example portion of the code in the first post. I didn't read all of the code, because it's too long to be a minimal example.

          But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

          There is no automatic size increase. Depending on the circumstances, a child may be overpainting the parent without obvious impact. Eg:

          BlueRect.qml:

          // Might look ok
          Item {
              height: 1
              //default clip = false
              Rectangle {
                  height: 100
                  border.color: "black"
                  border.width: 1
                  color: "blue"
              }
          }
          

          Repeated.qml:

          Repeater {
              model: 2
              // Delegate instances will overlap
              delegate: BlueRect {}
          }
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          K 1 Reply Last reply
          0
          • jeremy_kJ jeremy_k

            @Krulle said in QML List is not updated by QAbstractListModel:

            @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.

            I'm not clear on whether list is referring to the model (which has no on-screen dimensions), or the view (which has defined dimensions in the minimal example portion of the code in the first post. I didn't read all of the code, because it's too long to be a minimal example.

            But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

            There is no automatic size increase. Depending on the circumstances, a child may be overpainting the parent without obvious impact. Eg:

            BlueRect.qml:

            // Might look ok
            Item {
                height: 1
                //default clip = false
                Rectangle {
                    height: 100
                    border.color: "black"
                    border.width: 1
                    color: "blue"
                }
            }
            

            Repeated.qml:

            Repeater {
                model: 2
                // Delegate instances will overlap
                delegate: BlueRect {}
            }
            
            K Offline
            K Offline
            Krulle
            wrote on last edited by Krulle
            #5

            @jeremy_k said in QML List is not updated by QAbstractListModel:

            @Krulle said in QML List is not updated by QAbstractListModel:

            @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.

            I'm not clear on whether list is referring to the model (which has no on-screen dimensions), or the view (which has defined dimensions in the minimal example portion of the code in the first post. I didn't read all of the code, because it's too long to be a minimal example.

            But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

            There is no automatic size increase. Depending on the circumstances, a child may be overpainting the parent without obvious impact.

            Ok, I'll try with a minimal example:

            ListModel {
                    id: listMemberModel
            }
            
            ListView {
                    id: listMember
                    height: 0
                    model: listMemberModel
                    delegate: Item { height: 40; Textfield { text: name } }
            }
            

            work's with (only QML):

            listMemberModel.append({
                    "name": "Test"
            })
            

            but don't work with a cpp model derived from QAbstractListModel.
            If I append an Element via QML-style, the height of the listview is increasing. If i do the same operation from cpp, it doesn't.
            Do you have an idea why?

            JonBJ 1 Reply Last reply
            0
            • K Krulle

              @jeremy_k said in QML List is not updated by QAbstractListModel:

              @Krulle said in QML List is not updated by QAbstractListModel:

              @jeremy_k Thanks. I found the error myself tonight. The list had a height of 0.

              I'm not clear on whether list is referring to the model (which has no on-screen dimensions), or the view (which has defined dimensions in the minimal example portion of the code in the first post. I didn't read all of the code, because it's too long to be a minimal example.

              But I am still surprised that it still worked with a QML ListModel. Is the height automatically increased in this case?

              There is no automatic size increase. Depending on the circumstances, a child may be overpainting the parent without obvious impact.

              Ok, I'll try with a minimal example:

              ListModel {
                      id: listMemberModel
              }
              
              ListView {
                      id: listMember
                      height: 0
                      model: listMemberModel
                      delegate: Item { height: 40; Textfield { text: name } }
              }
              

              work's with (only QML):

              listMemberModel.append({
                      "name": "Test"
              })
              

              but don't work with a cpp model derived from QAbstractListModel.
              If I append an Element via QML-style, the height of the listview is increasing. If i do the same operation from cpp, it doesn't.
              Do you have an idea why?

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by
              #6

              @Krulle
              I don't do QML, so only a guess. Is the C++ end emitting a signal when it appends to the model, so that QML will know it has changed?

              jeremy_kJ 1 Reply Last reply
              0
              • JonBJ JonB

                @Krulle
                I don't do QML, so only a guess. Is the C++ end emitting a signal when it appends to the model, so that QML will know it has changed?

                jeremy_kJ Offline
                jeremy_kJ Offline
                jeremy_k
                wrote on last edited by
                #7

                The QML/Quick views respond to the same signals used by the widget views.

                Asking a question about code? http://eel.is/iso-c++/testcase/

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved