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
Forum Updated to NodeBB v4.3 + New Features

QML List is not updated by QAbstractListModel

Scheduled Pinned Locked Moved Solved QML and Qt Quick
7 Posts 3 Posters 1.3k 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 1 Jan 2023, 20:40 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 1 Jan 2023, 20:57 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 2 Jan 2023, 08:57
      0
      • jeremy_kJ jeremy_k
        1 Jan 2023, 20:57

        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 2 Jan 2023, 08:57 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 2 Jan 2023, 09:27
        0
        • K Krulle
          2 Jan 2023, 08:57

          @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 2 Jan 2023, 09:27 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 2 Jan 2023, 17:41
          0
          • jeremy_kJ jeremy_k
            2 Jan 2023, 09:27

            @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 2 Jan 2023, 17:41 last edited by Krulle 1 Feb 2023, 17:42
            #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 2 Jan 2023, 17:47
            0
            • K Krulle
              2 Jan 2023, 17:41

              @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 2 Jan 2023, 17:47 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 2 Jan 2023, 21:53
              0
              • JonBJ JonB
                2 Jan 2023, 17:47

                @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 2 Jan 2023, 21:53 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

                1/7

                1 Jan 2023, 20:40

                • Login

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