Unsolved Can I populate a TableView's rows with a C++ QVariantList?
-
I'm trying to make a recyclable TableView where each cell in a column can be editable based on the value (like a C# DataGridView).
I've started this off by trying to populate the table's rows using a QVariantList that is retrieved from a C++ class. However, I get a few error...Is there anyway to accomplish this without having to define a QAbstractTableModel in C++? My hopes was that I could just define the columns via QML like shown in my code.
Errors
qrc:/AppTableView.qml:10:12: QML TableModel: expected row for role "display" of TableModelColumn at index 0 to be a simple object, but it's QObject* instead: QVariant(QObject*, TableValue(0x20fa91742c0))
qrc:/AppTableView.qml:10:12: QML TableModel: expected row for role "display" of TableModelColumn at index 1 to be a simple object, but it's QObject* instead: QVariant(QObject*, TableValue(0x20fa91742c0))
qrc:/AppTableView.qml:0: ReferenceError: display is not defined
qrc:/AppTableView.qml:0: ReferenceError: display is not defined
qrc:/AppTableView.qml:0: ReferenceError: display is not defined
qrc:/AppTableView.qml:32: ReferenceError: display is not defined
qrc:/AppTableView.qml:32: ReferenceError: display is not defined
qrc:/AppTableView.qml:32: ReferenceError: display is not definedMy Code
AppTableView.qml
import QtQuick 2.15 import QtQuick.Controls 2.15 import Qt.labs.qmlmodels 1.0 TableView { id: root width: parent.width height: parent.height model: TableModel { TableModelColumn { display: "id" } TableModelColumn { display: "title" } // This does not work... rows: sampleController.tableValues /// This works, but is not populated by an existing collection of data... /*rows: [ { id: 0, title: "Title0" }, { id: 1, title: "Title1" } ]*/ } delegate: TextArea { id: textField text: display } }
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") AppTableView { } }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "SampleController.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); SampleController sampleController; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("sampleController", &sampleController); 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(); }
SampleController.h
#ifndef SAMPLECONTROLLER_H #define SAMPLECONTROLLER_H #include <QObject> #include <QVariantList> #include "SampleThread.h" class TableValue : public QObject { Q_OBJECT Q_PROPERTY(int id MEMBER m_id); Q_PROPERTY(QString title MEMBER m_title); public: explicit TableValue( const int id, const QString& title, QObject *parent = nullptr) : QObject(parent), m_id(id), m_title(title) { } private: int m_id; QString m_title; }; class SampleController : public QObject { Q_OBJECT Q_PROPERTY(QVariantList tableValues READ tableValues NOTIFY tableValuesChanged); public: explicit SampleController( QObject *parent = nullptr); const QVariantList tableValues() const; signals: void tableValuesChanged( const QVariantList& tableValues); private: QList<TableValue*> m_table_values; }; #endif // SAMPLECONTROLLER_H
SampleController.cpp
#include <QDebug> #include "SampleController.h" SampleController::SampleController( QObject *parent) : QObject(parent) { m_table_values.append(new TableValue(0, "Title0", this)); m_table_values.append(new TableValue(1, "Title1", this)); m_table_values.append(new TableValue(2, "Title2", this)); } const QVariantList SampleController::tableValues() const { QVariantList list; for(int i = 0; i < m_table_values.size(); i++) { list.append(QVariant::fromValue(m_table_values[i])); } return list; }
-
So I did some research and I didn't find any direct answer to my question... but with a little playing around I was able to produce what I wanted by changing AppTableView.qml a little.
Specifically, the TableModelColumn's now get the display via a function... I just don't understand why it doesn't work by directly providing the property name we are looking for (shown in my original post)... or even by doing return rows[modelIndex.row].id...
TableModelColumn { display: function(modelIndex) { return sampleController.tableValues[modelIndex.row].id } }
AppTableView.qml
TableView { id: root width: parent.width height: parent.height model: TableModel { TableModelColumn { display: function(modelIndex) { return sampleController.tableValues[modelIndex.row].id } } TableModelColumn { display: function(modelIndex) { return sampleController.tableValues[modelIndex.row].title } } // This does not work... rows: sampleController.tableValues // This works, but is not populated by an existing collection of data... /*rows: [ { id: 0, title: "Title0" }, { id: 1, title: "Title1" } ]*/ } delegate: TextArea { id: textField text: display } }
-
Anyone running into the same issue, aside from my solution above I could not find any other working solutions. That being said, even my solution above has issues...
For example, if I were to add a "Add Row" button that inserts a TableValue object into m_table_values. This addition is not reflected to the model view (and according to the documentation this isn't supported). However, if you are okay with just editing existing entries... this method works great (they update).
That being said, I ended up just making a basic QAbstractTableModel class and then made several others that inherit from my BasicTableModel... This produced what I wanted, but it's just very disappointing that this can't be accomplished via QML directly...
-
Thanks for providing the update.
I'm looking to do something similar at the moment, and am somewhat perplexed by the QAbstractItemModel approach. It seems overly complex for managing some basic data types in a QML TableView, and (from the initial research I've conducted) it seems that the 'roles' have to be pre-defined, which surely makes the use of a generic QAbstractItemModel-based class (i.e. one where the class can be instantiated for multiple QML TableViews within the one application, each with its own set of columns, etc.) either very complicated or not possible?