I am trying to get the Qt model/view-architecture working with a QML-View, but for whatever reason it is only partially working.
What works:
Not working:
What I am trying to do (for some weeks already), is to create a simple ApplicationView with a TableView and a C++-model, which is editable by the view.
Right now only a whole row is selectable, not a single cell. The table data doesn't seem to be editable at all. Can anyone give me a hint?
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TableView {
model: theModel
TableViewColumn {
role: "nameRole"
width: 75
}
TableViewColumn {
role: "ageRole"
width: 50
}
}
}
ModelItem.hpp
#ifndef MODELITEM
#define MODELITEM
#include <QString>
struct ModelItem {
ModelItem(QString name_, int age_)
: name(name_), age(age_) {}
QString name;
int age;
};
#endif // MODELITEM
TableModel.hpp
#ifndef TABLEMODEL_HPP
#define TABLEMODEL_HPP
#include <QAbstractTableModel>
#include "ModelItem.hpp"
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
TableModel(QObject *parent = 0);
//works
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
//does not work
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const;
enum Roles {
NameRole = Qt::UserRole + 1,
AgeRole
};
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
private:
QList<ModelItem*> items;
};
#endif // TABLEMODEL_HPP
TableModel.cpp
#include "TableModel.hpp"
TableModel::TableModel(QObject *parent)
: QAbstractTableModel(parent) {
items.append(new ModelItem("Hugo",33));
items.append(new ModelItem("Egon",34));
items.append(new ModelItem("Balder",66));
qDebug("TableModel initialisiert");
}
int TableModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
qDebug("columnCount");
return 2;
}
int TableModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
qDebug("rowCount");
return items.count();
}
QVariant TableModel::data(const QModelIndex &index, int role) const {
qDebug("data");
switch (role) {
case NameRole: return items[index.row()]->name;
case AgeRole: return items[index.row()]->age;
}
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const {
qDebug("headerData");
switch (role) {
case NameRole: return "1";
case AgeRole: return "2";
};
return QVariant();
}
QHash<int, QByteArray> TableModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NameRole] = "nameRole";
roles[AgeRole] = "ageRole";
qDebug("roleNames initialised");
return roles;
}
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const {
qDebug("--flags called--");
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
qDebug("setData called");
switch (role) {
case NameRole: items[index.row()]->name = value.toString();
case AgeRole: items[index.row()]->age = value.toInt();
}
emit dataChanged(index, index);
return true;
}
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "TableModel.hpp"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
TableModel model;
engine.rootContext()->setContextProperty("theModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
columnCount
and rowCount
called in QAbstractItemModel::index method, which is called by TableView before data
method.
QModelIndex QAbstractTableModel::index(int row, int column, const QModelIndex &parent) const
{
return hasIndex(row, column, parent) ? createIndex(row, column, 0) : QModelIndex();
}
bool QAbstractItemModel::hasIndex(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column < 0)
return false;
return row < rowCount(parent) && column < columnCount(parent);
}
columnCount
has no effect, as long is it is > 0, because TableView called the index
method with column always equal to 0.
headerData
and flags
did not affect the QML TableView. You can only set the headers on the QML side. To create an editable TableView, you should implement your custom itemDelegate
main.qml
ApplicationWindow {
visible: true
id: root
Component {
id: editableDelegate
Item {
Text {
width: parent.width
anchors.margins: 4
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
elide: styleData.elideMode
text: styleData.value !== undefined ? styleData.value : ""
color: styleData.textColor
visible: !styleData.selected
}
Loader {
id: loaderEditor
anchors.fill: parent
anchors.margins: 4
Connections {
target: loaderEditor.item
onEditingFinished: {
theModel.setData(styleData.row, styleData.column, loaderEditor.item.text)
}
}
sourceComponent: styleData.selected ? editor : null
Component {
id: editor
TextInput {
id: textinput
color: styleData.textColor
text: styleData.value
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}
}
}
}
TableView {
id: table
anchors.fill: parent
model: theModel
itemDelegate: editableDelegate;
TableViewColumn {
role: "nameRole"
width: 75
title: "name"
}
TableViewColumn {
role: "ageRole"
width: 50
title: "age"
}
}
}
To apply the changes to your model, you should implement the setData
method, like this:
TableModel.h
bool setData(const QModelIndex &index, const QVariant &value, int role) {
switch (role) {
case NameRole: items[index.row()]->name = value.toString(); break;
case AgeRole: items[index.row()]->age = value.toInt(); break;
}
emit dataChanged(index, index);
return true;
}
Q_INVOKABLE bool setData(int row, int column, const QVariant value)
{
int role = Qt::UserRole + 1 + column;
return setData(index(row,0), value, role);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With