Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Editable TreeView does not update on dataChanged emitted
Forum Updated to NodeBB v4.3 + New Features

Editable TreeView does not update on dataChanged emitted

Scheduled Pinned Locked Moved Solved QML and Qt Quick
2 Posts 1 Posters 341 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • enelE Offline
    enelE Offline
    enel
    wrote on last edited by
    #1

    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:
    04cfa579-14e3-4d4f-95cc-17a2494a2f46-image.png

    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;
    }
    
    1 Reply Last reply
    0
    • enelE Offline
      enelE Offline
      enel
      wrote on last edited by
      #2

      Finally found the missing piece: Item needs to emit dataChanged in setData():

      void setData(const QVariant &value, int role) override;
      
      1 Reply Last reply
      0
      • enelE enel has marked this topic as solved on

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved