Here is the example:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlContext *context = engine.rootContext();
myModel *model = new myModel();
context->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();
}
mymodel.h
#ifndef MYMODEL_H
#define MYMODEL_H
#include <QStandardItemModel>
#include <QObject>
#include <QDebug>
#define ROWS 10
#define NESTED 3
#define SUBITEMS 8
class myModel : public QStandardItemModel
{
Q_OBJECT
public:
myModel();
void fillModel();
QVariantMap getObject(const QModelIndex &index);
QVariantMap getObject(QStandardItem *item);
Q_INVOKABLE QVariant getQMLObject(const QModelIndex &index);
Q_INVOKABLE void setValue(const QModelIndex &index, int value);
enum Roles{
Name=Qt::UserRole+1,
Value,
Description,
QMLObject
};
public slots:
void updateObject(const QModelIndex &left, const QModelIndex &right, const QVector<int> &roles);
private:
QHash<int, QByteArray> roleNames;
QHash<int,QVector<QPersistentModelIndex>> m_Objects;
};
#endif // MYMODEL_H
mymodel.cpp
#include "mymodel.h"
myModel::myModel()
{
roleNames[Name] = "name";
roleNames[Value] = "value";
roleNames[Description] = "description";
roleNames[QMLObject] = "qmlObject";
setItemRoleNames(roleNames);
fillModel();
connect(this, &QAbstractItemModel::dataChanged, this, &myModel::updateObject);
}
void myModel::fillModel()
{
for(int i = 0; i < 3; i++){
QStandardItem *item = new QStandardItem();
item->setData("TreeParameter"+QString::number(i+1), Name);
item->setData(i, Value);
item->setData("Tree item"+QString::number(i+1), Description);
item->setData(getObject(item), QMLObject);
for(int j = 0; j < 8; j++){
QStandardItem *subItem = new QStandardItem();
subItem->setData("SubItem"+QString::number(j+1), Name);
subItem->setData(j, Value);
subItem->setData("Nested item"+QString::number(j+1), Description);
subItem->setData(getObject(subItem), QMLObject);
item->appendRow(subItem);
}
this->appendRow(item);
}
for(int i = 3; i < 10; i++){
QStandardItem *item = new QStandardItem();
item->setData("Parameter"+QString::number(i+1), Name);
item->setData(i, Value);
item->setData("Usual item"+QString::number(i+1), Description);
item->setData(getObject(item), QMLObject);
this->appendRow(item);
}
}
QVariantMap myModel::getObject(const QModelIndex &index)
{
return getObject(itemFromIndex(index));
}
QVariantMap myModel::getObject(QStandardItem *item)
{
QVariantMap result;
result[roleNames[Name]] = item->data(Name);
result[roleNames[Value]] = item->data(Value);
result[roleNames[Description]] = item->data(Description);
return result;
}
QVariant myModel::getQMLObject(const QModelIndex &index)
{
return index.data(QMLObject);
}
void myModel::setValue(const QModelIndex &index, int value)
{
setData(index, value, Value);
}
void myModel::updateObject(const QModelIndex &left, const QModelIndex &right, const QVector<int> &roles)
{
if(roles[0] == QMLObject) return;
QVariantMap obj = left.data(QMLObject).toMap();
obj[roleNames[roles[0]]] = left.data(roles[0]);
setData(left, obj, QMLObject);
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4 as QC1
import QtQuick.Controls 2.12
import QtQml.Models 2.11
Window {
width: 640
height: 280
visible: true
title: qsTr("Hello World")
RowLayout{
anchors.fill: parent
QC1.TreeView{
id: tree
Layout.fillWidth: true;
Layout.fillHeight: true;
model: myModel
selection: ItemSelectionModel{
id:mySelectionModel
model: myModel
}
itemDelegate: ItemDelegate{
Text{
text: styleData.value
}
MouseArea{
anchors.fill: parent
onClicked:{
tree.selection.setCurrentIndex(styleData.index,ItemSelectionModel.SelectCurrent);
target.modelItem = myModel.getQMLObject(styleData.index);
target.modelIndex = styleData.index;
}
}
}
QC1.TableViewColumn{
width: 150
title: "Name"
role: "name"
}
QC1.TableViewColumn{
width: 50
title: "Value"
role: "value"
delegate: TextEdit{
text: styleData ? styleData.value : "";
Keys.onReturnPressed: {
focus = false;
myModel.setValue(styleData.index, text);
}
}
}
QC1.TableViewColumn{
width: 150
title: "Description"
role: "description"
}
}
RowLayout{
id: target
Layout.fillWidth: true;
property var modelIndex: false
property var modelItem: false
Rectangle{
implicitHeight: 20
implicitWidth: 65
border.color: "black";
border.width: 1
TextEdit{
id: targetValue
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: target.modelItem ? target.modelItem.value : "NaN"
Keys.onReturnPressed: {
targetValue.focus = false;
if(target.modelIndex) myModel.setValue(target.modelIndex, targetValue.text);
}
}
}
TextArea{
id: targetText
readOnly: true
implicitWidth: 130
wrapMode: Text.Wrap
text: target.modelItem ? target.modelItem.name : "Nothing yet"
}
}
}
}
Guess I'll have to explain it a little bit. When I click an item of the model in TreeView its QMLObject role is sent to target component, where Value is given to TextEdit and Name is given to TextArea along with the QModelIndex of selected item. So when I change the value inside of TextEdit it updates the value in model. The issue here is that if I change the value inside TreeView it won't be updated in TextEdit, because target receives only a copy of QMLObject role through return index.data(QMLObject);. I know about possibility to pass QQmlDMAbstractItemModelData from TreeView to the taget, but I'm trying to bind model item to target directly, this TreeView is only to make this example more clear. Sorry, couldn't think of something smaller, guess I'm too deep in this topic to keep it simple