QML List is not updated by QAbstractListModel
-
wrote on 1 Jan 2023, 20:40 last edited by
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-fileimport 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) } } }
-
wrote on 1 Jan 2023, 20:57 last edited by
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.
-
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.
-
@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?wrote on 2 Jan 2023, 09:27 last edited by@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 {} }
-
@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 {} }
wrote on 2 Jan 2023, 17:41 last edited by Krulle 1 Feb 2023, 17:42@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? -
@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? -
@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?wrote on 2 Jan 2023, 21:53 last edited byThe QML/Quick views respond to the same signals used by the widget views.
1/7