Reputation: 31
I am relatively new to QT, so any help would be greatly appreciated!
I am working on a Qt Quick Application, making use of the QQmlApplicationEngine for the UI. I made a subclass of QAbstractTableModel and implemented the necessary functions and successfully created and displayed a (singular) table on the Window.
Currently, how I linked the model in the QML file is by setting the context property of the root property of QQmlApplicationEngine.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSharedPointer>
#include <QQmlContext>
#include "tablecontroller.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QSharedPointer<QQmlApplicationEngine> engine =
QSharedPointer<QQmlApplicationEngine>::create();
TableController theController(engine.toWeakRef());
engine.data()->rootContext()->setContextProperty("TableController", &theController);
engine.data()->load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
tabcontroller.h
#ifndef TABLECONTROLLER_H
#define TABLECONTROLLER_H
#include <QObject>
#include <QQmlApplicationEngine>
#include <QWeakPointer>
#include <QHash>
#include <QList>
#include "tablemodel.h"
class TableController : public QObject
{
Q_OBJECT
public:
explicit TableController(QWeakPointer<QQmlApplicationEngine> Engine, QObject *parent = 0);
Q_INVOKABLE void AddEntry();
signals:
public slots:
private:
TE::TDT::TableModel m_TableModel;
QList<QString> m_Headings;
};
#endif // TABLECONTROLLER_H
tabcontroller.cpp
#include "tablecontroller.h"
#include <QQmlContext>
TableController::TableController(QWeakPointer<QQmlApplicationEngine> Engine, QObject *parent) : QObject(parent)
{
m_Headings << "Heading1" << "Heading2" << "Heading3" << "Heading4";
m_TableModel.setColumnHeadings(m_Headings);
Engine.data()->rootContext()->setContextProperty("myModel", &m_TableModel);
}
void TableController::AddEntry()
{
QHash<QString, QVariant> tempHash;
int counter = 1;
for (auto x : m_Headings)
{
tempHash.insert(x, QString::number(counter));
counter++;
}
m_TableModel.addElement(tempHash);
}
tablemodel.h
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QObject>
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QList>
#include <QString>
#include <QHash>
namespace TE {
namespace TDT {
class TableModel : public QAbstractTableModel
{
Q_OBJECT
Q_PROPERTY(QStringList userRoleNames READ userRoleNames CONSTANT)
public:
explicit TableModel(QObject *parent = 0);
enum MyModelRoles {
UserRole1 = Qt::UserRole + 1,
UserRole2,
};
void setFirstColumn(const QList<QString> &FirstColumn);
void setColumnHeadings(const QList<QString> &ColumnHeadings);
void addElement(const QHash<QString, QVariant> Entry);
QStringList userRoleNames();
signals:
public slots:
// QAbstractTableModel interface
public:
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QHash<int, QByteArray> roleNames() const override;
private:
QList<QHash<QString, QVariant>> m_TableData;
QList<QString> m_ColumnHeadings;
QMap<int, QString> m_roleNames;
};
}// TDT
}// TE
#endif // TABLEMODEL_H
tablemodel.cpp
#include "tablemodel.h"
#include <QDebug>
#include <QAbstractListModel>
TE::TDT::TableModel::TableModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int TE::TDT::TableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_TableData.count();
}
int TE::TDT::TableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_ColumnHeadings.count();
}
QVariant TE::TDT::TableModel::data(const QModelIndex &index, int role) const
{
QVariant retVal;
try {
if(!index.isValid())
{
throw QString("Invalid index for inherited data function");
}
// Check row index
if(index.row() >= m_TableData.count() || index.row() < 0)
{
throw QString("Index (row) out of bounds for data function");
}
//Check column index
if(index.column() >= m_ColumnHeadings.count() || index.column() < 0)
{
throw QString("Index (column) out of bounds for data function");
}
QList<int> keys = m_roleNames.keys();
if(role == Qt::DisplayRole || role == Qt::EditRole)
{
QString colKey = m_ColumnHeadings.at(index.column());
if (m_TableData.at(index.row()).value(colKey).isNull())
{
retVal = QVariant();
} else {
retVal = m_TableData.at(index.row()).value(colKey);
}
} else if (m_roleNames.keys().contains(role)) {
QHash<QString, QVariant> temp1 = m_TableData.at(index.row());
retVal = m_TableData.at(index.row()).value(m_roleNames.value(role));
}
return retVal;
} catch (QString &e) {
qDebug() << e;
return QVariant();
}
}
QVariant TE::TDT::TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation);
QVariant retVal;
if (role == Qt::DisplayRole)
{
retVal = m_ColumnHeadings.at(section);
}
return retVal;
}
QHash<int, QByteArray> TE::TDT::TableModel::roleNames() const {
// Populate the roles - basically the column headings
QHash<int, QByteArray> roles = QAbstractTableModel::roleNames();
// Should not overwrite existing roles
int LastIndexOfUserRole = Qt::UserRole;
for (int x = 1; x <= m_ColumnHeadings.count(); x++)
{
roles[LastIndexOfUserRole + x] = m_ColumnHeadings.at(x-1).toUtf8();
}
return roles;
}
QStringList TE::TDT::TableModel::userRoleNames() // Return ordered List of user-defined roles
{
QHashIterator<int, QByteArray> i(roleNames());
while (i.hasNext())
{
i.next();
if(i.key() > Qt::UserRole)
{
m_roleNames[i.key()] = i.value();
}
}
return m_roleNames.values();
}
void TE::TDT::TableModel::setColumnHeadings(const QList<QString> &ColumnHeadings)
{
m_ColumnHeadings = ColumnHeadings;
}
void TE::TDT::TableModel::addElement(const QHash<QString, QVariant> Entry)
{
beginInsertRows(QModelIndex(), this->rowCount(QModelIndex()), this->rowCount(QModelIndex()));
m_TableData.append(Entry);
endInsertRows();
}
main.qml import QtQuick 2.5 import QtQuick.Window 2.2 import QtQuick.Controls 1.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: mainwindow
// template component for the column headings
Component
{
id: columnComponent
TableViewColumn{width: 100 }
}
TableView {
id: tableview
height: mainwindow.height - 50
width: mainwindow.width
y: 5
x: 0
visible: true
resources:
{
var roleList = myModel.userRoleNames
var temp = []
for(var i = 0; i < roleList.length; i++)
{
var role = roleList[i]
temp.push(columnComponent.createObject(tableview, { "role": role, "title": role}))
}
return temp
}
model: myModel
}
Rectangle {
id: abutton
anchors.top: tableview.bottom
height: 40
width: mainwindow.width
Text {
text: "Click to Add"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
TableController.AddEntry()
}
}
}
}
Code adapted from: QML TableView with dynamic number of columns
Now, my question is, if I wanted to re-use the TableView as that defined in the main.qml I want to use another model for it. The problem is (from my limited understanding) that the "variable" that the model in the QML links to is static (defined at start-up), in this case, "myModel".
How can I change the model once I create another instance of this TableView? Will I have to link another "variable" every time?
I have tried to cast the TableView (in QML) to a QQuickItem (in c++) and trying to set the property there, to no avail (gives a null, but also QQuickItem doesn't have a "setModel" function)
Sorry for the long post, wanted to give as much information as possible.
Upvotes: 1
Views: 2631
Reputation: 31
https://stackoverflow.com/a/35755172/7094339
This guy saved my life :D So it appears I needed to use the component.beginCreate() function.
Upvotes: 0
Reputation: 7150
If I understand your question correctly, you need to reuse your custom TableView
with another model, you don't really need to change the model
of your existing TableView
.
For that you can define a new component in a separate file and use it elsewhere (some doc here : http://doc.qt.io/qt-5/qtqml-documents-definetypes.html)
In your case it could look like that :
DynamicTableView.qml
:
TableView {
//it's better not to set positioning properties in a component definition file.
Component {
id: columnComponent
TableViewColumn { width: 100 }
}
resources:
{
var roleList = model.userRoleNames // here you expect all your models to be an instance of your TableModel
var temp = []
for(var i = 0; i < roleList.length; i++)
{
var role = roleList[i]
temp.push(columnComponent.createObject(tableview, { "role": role, "title": role}))
}
return temp
}
}
You could then reuse your component in main.qml
and define its model
property :
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: mainwindow
DynamicTableView {
id: tableview
height: mainwindow.height - 50
width: mainwindow.width
y: 5
x: 0
model: myModel
}
Rectangle {
id: abutton
anchors.top: tableview.bottom
height: 40
width: mainwindow.width
Text {
text: "Click to Add"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
TableController.AddEntry()
}
}
}
}
Upvotes: 0
Reputation: 5207
As @folibis said, one option is to make your model instantiable from QML, i.e. so that you can craete instances of the model in QML.
You can then add a method to the TableController
to "register" these instances in case the controller needs to be aware of them.
Alternatively you still create the models inside TableController
and make them accessible, e.g. via one property for each model, a list property or a Q_INVOKABLE
method.
Upvotes: 0