c++qtqmlqt5qstandarditemmodel

How to access nested QStandardItemModel's items from QML?


Background

I have a tree-like QStandardItemModel, whose items I would like to access from QML.

Here is how the model is defined on the C++ side:

backend.h

class Backend : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QStandardItemModel *model READ model CONSTANT)
public:
    explicit Backend(QObject *parent = nullptr);

    QStandardItemModel *model() const;

private:
    QStandardItemModel *m_model;
};

backend.cpp

Backend::Backend(QObject *parent) :
    QObject(parent),
    m_model(new QStandardItemModel(this))
{
    auto *itemFirst = new QStandardItem(tr("First"));
    auto *itemSecond = new QStandardItem(tr("Second"));
    auto *subItem = new QStandardItem(tr("First_02"));

    subItem->appendRow(new QStandardItem("First_02_01"));

    itemFirst->appendRow(new QStandardItem(tr("First_01")));
    itemFirst->appendRow(subItem);
    itemFirst->appendRow(new QStandardItem(tr("First_03")));

    itemSecond->appendRow(new QStandardItem(tr("Second_00")));
    itemSecond->appendRow(new QStandardItem(tr("Second_01")));

    m_model->appendRow(itemFirst);
    m_model->appendRow(itemSecond);
}

QStandardItemModel *Backend::model() const
{
    return m_model;
}

The model is exported to QML in main.cpp like this:

Backend backend;
QQmlApplicationEngine engine;

engine.rootContext()->setContextProperty("backend", &backend);
qmlRegisterUncreatableType<QStandardItemModel>("QStandardItemModel", 1, 0, "QStandardItemModel", "The model should be created in C++");

Using TreeView from QtQuick.Controls 1.4 in main.qml like this:

TreeView {
    anchors.fill: parent
    model: backend.model

    TableViewColumn {
        title: "Name"
        role: "display"
    }
}

I get the desired results, i.e. all items nested correctly:

Output of TreeView

Problem

When I try to manually iterate over the nested items using Repeater and DelegateModel like this:

ColumnLayout {
    anchors.fill: parent

    Repeater {
        model: backend.model

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            ColumnLayout {
                anchors.fill: parent

                Text {
                    color: "blue"
                    text: model.display
                }

                Repeater {
                    model: DelegateModel {
                        model: backend.model
                        rootIndex: modelIndex(index)

                        Item {
                            Layout.fillWidth: true
                            Layout.fillHeight: true

                            ColumnLayout {
                                anchors.fill: parent

                                Text {
                                    color: "green"
                                    text: model.display
                                }

                                Repeater {
                                    model: DelegateModel {
                                        model: backend.model
                                        rootIndex: modelIndex(index)

                                        Text {
                                            color: "red"
                                            text: model.display
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

the main branches (marked with blue) and the items on the first nesting level (marked with green) are the right ones, but I get the wrong items on the second nesting level (marked with red):

Output of the manual iteration

How to fix the code to correctly iterate over the QStandardItemModel's items on each nesting level?


Solution

  • The problem is in these two lines: rootIndex: modelIndex(index).

    index is the index of the 'parent' model, but modelIndex(...) is the method of the current model.


    I've tried it with this (slightly modified) piece of code and it worked:

    Repeater {
        model: DelegateModel {
            id: model1
    
            model: backend.model
    
            delegate: ColumnLayout{
                Text {
                    text: "Data: " + display
                }
                Repeater {
                    model: DelegateModel {
                        id: model2
    
                        model: backend.model
                        // 'index' comes from 'model1', so use the 'modelIndex' method from 'model1'
                        rootIndex: model1.modelIndex(index)
    
                        delegate: ColumnLayout{
                            Text {
                                text: "- Data: " + display
                            }
                            Repeater {
                                model: DelegateModel {
                                    id: model3
    
                                    model: backend.model
                                    // 'index' comes from 'model2', so use the 'modelIndex' method from 'model2'
                                    rootIndex: model2.modelIndex(index)
    
                                    delegate: Text {
                                        text: "--  Data: " + display
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }