Editable TreeView does not update on dataChanged emitted
-
For a tree of on/off settings, parents should be able to set all children at once, setting a child should update the parent check state:
When a single CheckBox is toggled, its child or parent item is updated correctly in the Item class.
However, the QML TreeView does not reflect the parent or child of the changed item as expected.I am aware, that there is a CheckState in the QStandardItem, which I do not want to use. But that should not make a difference.
Any hints what I am doing wrong?
I have tried using layoutChanged instead of dataChanged, which does not change results.import QtQuick import QtQuick.Window import QtQuick.Layouts import QtQuick.Controls Window { id: root width: 300 height: 300 visible: true title: qsTr("Tree") TreeView { width: parent.width height: parent.height model: TreeModel {} Component.onCompleted: expandRecursively() delegate: Item { implicitHeight: rowLayout.height implicitWidth: root.width required property int depth RowLayout { id: rowLayout CheckBox { id: cbActive Layout.leftMargin: (depth + 1) * 10 bottomPadding: 5 rightPadding: 0 topPadding: 5 checked: model.Active onCheckStateChanged: model.Active = checked } Label { id: lblField text: model.Name } } } } }
#ifndef TREEMODEL_H #define TREEMODEL_H #include <QObject> #include <QQmlEngine> #include <QStandardItemModel> // Item class Item : public QStandardItem { public: explicit Item(const QString& name, bool active); ~Item() = default; void setActive(bool active); bool active() const; QString name() const; private: QString m_name; bool m_active; }; // Model class TreeModel : public QStandardItemModel { Q_OBJECT QML_ELEMENT public: TreeModel(); ~TreeModel() = default; enum { NameRole = Qt::UserRole + 1, ActiveRole, }; QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QHash<int, QByteArray> roleNames() const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; private: QHash<int, QByteArray> m_roleNames; }; #endif // TREEMODEL_H
#include "treemodel.h" // Item Item::Item(const QString& name, bool active) : QStandardItem() , m_name(name) , m_active(active) {} void Item::setActive(bool active) { m_active = active; } bool Item::active() const { return m_active; } QString Item::name() const { return m_name; } // Model TreeModel::TreeModel() { m_roleNames[NameRole] = "Name"; m_roleNames[ActiveRole] = "Active"; auto parent = new Item("Parent", true); auto child1 = new Item("Child 1", true); auto root = invisibleRootItem(); root->appendRow(parent); parent->appendRow(child1); } QVariant TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (auto item = dynamic_cast<Item*>(itemFromIndex(index)); item != nullptr) { switch (role) { case NameRole: return item->name(); case ActiveRole: return item->active(); } } return QStandardItemModel::data(index, role); } Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; return Qt::ItemIsEditable | QAbstractItemModel::flags(index); } QHash<int, QByteArray> TreeModel::roleNames() const { return m_roleNames; } bool TreeModel::setData(const QModelIndex &modelIndex, const QVariant &value, int role) { if (!modelIndex.isValid() || role != ActiveRole) return false; if (auto item = dynamic_cast<Item*>(itemFromIndex(modelIndex)); item != nullptr) { item->setActive(value.toBool()); if (item->hasChildren()) { for (auto r = 0; r < rowCount(modelIndex); ++r) { const auto cIndex = index(r, 0, modelIndex); if (auto cItem = dynamic_cast<Item*>(itemFromIndex(cIndex))) cItem->setActive(value.toBool()); } } if (item->parent() != nullptr) { if (auto parent = dynamic_cast<Item*>(item->parent()); parent != nullptr) parent->setActive(value.toBool()); } emit dataChanged(QModelIndex(), QModelIndex()); return true; } return false; }
-
E enel has marked this topic as solved on