3-dimensional model
-
Hi there,
I have a set of data that I want to display as a list (in a non-qml sense).
One line will contain the following :
- a boolean -> Checkbox (not represented in the code below)
- a text for description -> Text
- a list of datetime (or list of string)-> Combobox
In my mind, this looks like a 3-dimensional data: so I assume QAbstractItemModel would fit but can find a solution.
I struggle with the data in the combobox. Here is my code:
main.qml
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { width: 640 height: 480 visible: true title: qsTr("3D Model") ListView{ anchors.fill:parent model: mymodel clip:true delegate: Rectangle{ width:parent.width; height:50; color: model["color"] Text{ id:txt height: parent.height anchors.left: parent.left anchors.right: cbb.left text: model["display"] verticalAlignment: Text.AlignVCenter } ComboBox{ id:cbb width: 200 height: parent.height anchors.right: parent.right model: model["runList"] delegate: Text{ text: "display" } } } } }
testmodel.cpp
#include "testmodel.h" #include <QDateTime> #include <QDebug> TestModel::TestModel() { qDebug() << Q_FUNC_INFO; QList<QString> dateTimeList; dateTimeList.append(QDateTime::currentDateTime().toString(Qt::ISODate)); for (quint8 i=0; i < 20; i++){ dateTimeList.append(QDateTime::currentDateTime().toString(Qt::ISODate)); Test* test = new Test(QString("%1 - Test Number %1").arg(i), dateTimeList); m_testList.append(test); } } int TestModel::rowCount(const QModelIndex &parent) const { return m_testList.count(); } QVariant TestModel::data(const QModelIndex &index, int role) const { switch (role){ case Qt::DisplayRole: return m_testList.at(index.row())->name(); case Qt::DecorationRole: return m_testList.at(index.row())->color(); case Qt::UserRole+1: return m_testList.at(index.row())->runList(); } return QVariant(); } QHash<int, QByteArray> TestModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "display"; roles[Qt::DecorationRole] = "color"; roles[Qt::UserRole+1] = "runList"; return roles; }
testmodel.h
#ifndef TestModel_H #define TestModel_H #include <QAbstractListModel> #include "test.h" class TestModel : public QAbstractListModel { Q_OBJECT public: TestModel(); public: // QAbstractItemModel interface virtual int rowCount(const QModelIndex &parent) const override; virtual QVariant data(const QModelIndex &index, int role) const override; virtual QHash<int, QByteArray> roleNames() const override; private: QList<Test*> m_testList; }; #endif // TestModel_H
test.cpp
#include "test.h" #include <QRandomGenerator> #include <QDebug> Test::Test(const QString &newName, const QStringList &newRunList): m_name(newName), m_runList(newRunList) { int red = QRandomGenerator::global()->bounded(0, 256); int green = QRandomGenerator::global()->bounded(0, 256); int blue = QRandomGenerator::global()->bounded(0, 256); m_color = QColor(red, green, blue); qDebug() << Q_FUNC_INFO << "Creating " << m_name << "with color: " << m_color; } const QString &Test::name() const { return m_name; } void Test::setName(const QString &newName) { m_name = newName; } const QStringList &Test::runList() const { return m_runList; } void Test::setRunList(const QStringList &newRunList) { m_runList = newRunList; } const QColor &Test::color() const { return m_color; }
test.h
#ifndef Test_H #define Test_H #include <QString> #include <QStringList> #include <QColor> class Test { public: Test(const QString &newName, const QStringList &newRunList); const QString &name() const; void setName(const QString &newName); const QStringList &runList() const; void setRunList(const QStringList &newRunList); const QColor &color() const; private: QString m_name; QStringList m_runList; QColor m_color; }; #endif // Test_H
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QDebug> #include "testmodel.h" int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QGuiApplication app(argc, argv); qmlRegisterType<TestModel> ("TModel", 1, 0, "TestModel"); qDebug() << Q_FUNC_INFO; TestModel model ; qDebug() << Q_FUNC_INFO; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("mymodel", &model); const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
An advise would be appreciated.
Yrog
-
I got your original code working:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { width: 640 height: 480 visible: true title: qsTr("3D Model") ListView{ anchors.fill:parent model: mymodel clip:true delegate: Rectangle{ id: rect property var sourcemodel: model width: parent.width; height: 50 color: sourcemodel.color Text{ id:txt height: parent.height anchors.left: parent.left anchors.right: cbb.left text: display verticalAlignment: Text.AlignVCenter } ComboBox{ id:cbb width: 200 height: parent.height anchors.right: parent.right model: runList delegate: Text{ text: modelData } } } } }
Look carefully how I used the role names. For instance using "color" as a role name is not a good idea. runList and display are fine because those names don't typically collide. Also note where I used modelData for the ComboBox delegate. This is because it is a list of items rather than a full blown item model.
-
ComboBox{ id:cbb width: 200 height: parent.height anchors.right: parent.right model: model["runList"] delegate: Text{ text: modelData // "display" is just a string } }
modelData is where the data is placed when feeding it a list without. Otherwise the data follows the roleNames.
Also:
model: model.runList // really only needed if there is ambiguous naming going on or model: runList // when using a proper model you should be able to use role name like this
-
Thanks for your reply. But...
qrc:/main.qml:34: TypeError: Cannot read property 'runList' of undefined
The role "runList" seems correct as I tried model["runList"] in Text{}, the trace in Test::runList() returns the list (but Text is unable to display a list :) )
-
Many thanks @fcarney for the time spent on my issue.
I finally changed my mind and simplified the problem. I converted the model in model["runList"] in a basic QStringList which is enough for my needs right now. Not the most elegant way to solve my issue but it works.
But I can't just get a working solution for the model in model, even with your suggestions. Quite frustrating.
Thanks again
-
I got your original code working:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { width: 640 height: 480 visible: true title: qsTr("3D Model") ListView{ anchors.fill:parent model: mymodel clip:true delegate: Rectangle{ id: rect property var sourcemodel: model width: parent.width; height: 50 color: sourcemodel.color Text{ id:txt height: parent.height anchors.left: parent.left anchors.right: cbb.left text: display verticalAlignment: Text.AlignVCenter } ComboBox{ id:cbb width: 200 height: parent.height anchors.right: parent.right model: runList delegate: Text{ text: modelData } } } } }
Look carefully how I used the role names. For instance using "color" as a role name is not a good idea. runList and display are fine because those names don't typically collide. Also note where I used modelData for the ComboBox delegate. This is because it is a list of items rather than a full blown item model.
-
Thanks,
Your solution works !
But the two lines below were not a concern as color: model["color"] worked fine
property var sourcemodel: model color: sourcemodel.color
what solved my issue in your response is
model: runList
My understanding was that model["runList"] and model: runList were equivalent, just needed for desambiguation. I had wrong but don't know why...
@fcarney thanks for your patience, that solved my issue !