Create Model for QML TreeView

2019-05-30 00:35发布

I'm trying to use QML TreeView Model. The example from Qt doesn't include how to create the model. I read this post and tried to use the code from @Tarod but the result is not what I expected.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "animalmodel.h"
#include <qqmlcontext.h>
#include <qqml.h>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    AnimalModel model;
    model.addAnimal("wolf", "Medium");
    model.addAnimal("Bear", "Large");

    QQmlApplicationEngine engine;
    QQmlContext *ctxt = engine.rootContext();
    ctxt->setContextProperty("myModel", &model);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

animalmodel.h

#ifndef ANIMALMODEL_H
#define ANIMALMODEL_H

#include <QStandardItemModel>


class AnimalModel : public QStandardItemModel
{
    Q_OBJECT //The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
public:
    enum AnimalRoles {
        TypeRole = Qt::UserRole + 1,
        SizeRole
    };

    AnimalModel(QObject *parent = 0);

    Q_INVOKABLE void addAnimal(const QString &type, const QString &size);

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

protected:
    QHash<int, QByteArray> roleNames() const;
};

#endif // ANIMALMODEL_H

animalmodel.cpp

#include "animalmodel.h"

AnimalModel::AnimalModel(QObject *parent)
    : QStandardItemModel(parent)
{

}

void AnimalModel::addAnimal(const QString &type, const QString &size)
{
    QStandardItem* entry = new QStandardItem();
    entry->setData(type, TypeRole);

    auto childEntry = new QStandardItem();
    childEntry->setData(size, SizeRole);
    entry->appendRow(childEntry);

    appendRow(entry);
}

QVariant AnimalModel::data(const QModelIndex & index, int role) const {
    QStandardItem *myItem = itemFromIndex(index);

    if (role == TypeRole)
        return myItem->data(TypeRole);
    else if (role == SizeRole) {
        if (myItem->child(0) != 0)
        {
            return myItem->child(0)->data(SizeRole);
        }
    }
    return QVariant();
}

QHash<int, QByteArray> AnimalModel::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[TypeRole] = "type";
    roles[SizeRole] = "size";
    return roles;
}

main.qml

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.4


ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    menuBar: MenuBar {
        Menu {
            title: qsTr("&File")
            MenuItem {
                text: qsTr("&Open")
                onTriggered: messageDialog.show(qsTr("Open Action Triggered"));
            }
            MenuItem {
                text: qsTr("&Exit")
                onTriggered: Qt.quit();
            }
        }
    }


    TreeView {
        anchors.fill: parent
        model: myModel
        TableViewColumn {
            title: "Name"
            role: "type"
            width: 300
        }
        TableViewColumn {
            title: "Size"
            role: "size"
            width: 300
        }
    }
}

What I got is something like this: Result

What I want to have is the animal size as a child of animal type.

1条回答
干净又极端
2楼-- · 2019-05-30 01:14

Model sub-classing is one of the worst minefields in Qt. The advice is always to have it go through the model test (https://wiki.qt.io/Model_Test) to see if everything was implemented correctly.

On the other hand, in 90% of the cases you do not need to subclass a model at all as the default models provided by Qt work quite well. What I'd do is just use QStandardItemModel using, on the C++ side, only the QAbstractItemModel interface (i.e. force yourself to use QAbstractItemModel* model = new QStandardItemModel(/*parent*/);) this way, if in the future you feel like you really need to reimplement the model (for efficiency) you'll just need to change 1 line in your existing code.

In your case:

void AnimalModel::addAnimal(const QString &type, const QString &size)
{
    if(columnCount()==0) insertColumn(0); // make sure there is at least 1 column
    insertRow(rowCount()); // add a row to the root
    const QModelIndex addedIdx = index(rowCount()-1,0);
    setData(addedIdx, type, TypeRole); // set the root data
    insertRow(rowCount(addedIdx),addedIdx ); // add 1 row ...
    insertColumn(0,addedIdx ); // ... and 1 column to the added root row
    setData(index(0,0,addedIdx), size, SizeRole); // set the data to the child

}
查看更多
登录 后发表回答