这是 QML 中的最小可行 TreeView 模型吗?

2023-12-06

我正在制作一个包含三个项目的折叠列表:“嘿”、“什么”和“起来?”。我想把它放到树视图中。我知道这个清单会只包含这三项。因此,我想知道如何将这些项目“嵌套”在一起。

我知道敏捷系统有一些实现,支持添加和删除父/子对象、查找索引......强大的模型。但是,我实际上只需要在可扩展/可折叠视图中显示这些项目。以下是我读过的有关 C++ 和 QAbstractItemModels 的内容:

  • QML 树视图
  • QML QAbstractItemModel
  • My_Cat_Jessica 提出的这个问题
  • 卡瓦列罗提出的这个问题这是基于:
  • 这个“有效”的例子由 Qt 本身提供(实际上不适用于 TreeView。但适用于 QTreeView!)

这是使用模型实现树视图的最简单可行的代码:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4


Window {
    id: mywindow
    visible: true
    width: 640
    height: 480

    TreeView {
        id: treeview
        anchors.fill: parent

        TableViewColumn {
            title: "Phrase"
            role: "phrase"
        }
        model: phraseModel
    }

    ListModel {
        id: phraseModel
        ListElement { phrase: "Hey"; }
        ListElement { phrase: "What's"; }
        ListElement { phrase: "Up?"; }
    }
}

我希望输出产生如下所示的嵌套堆栈:

Hey
    What's
        Up?

但我将所有内容都放在一列中,并且彼此对齐:

Hey
What's
Up?

我知道我还没有指定父母,而且我不完全确定该怎么做 - 但我什至不确定是否就是这样needs完成此代码。所以我的问题是:将这三个元素堆叠到可扩展/可折叠视图中缺少的最后一步是什么?


没有可以使用 TreeView 的原生 QML 模型,因此我实现了一个尝试通用的模型:

树元素

// treeelement.h
#ifndef TreeElement_H
#define TreeElement_H

#include <QObject>
#include <QQmlListProperty>

class TreeElement : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(QQmlListProperty<TreeElement> items READ items)
    Q_CLASSINFO("DefaultProperty", "items")
    TreeElement(QObject *parent = Q_NULLPTR);

    Q_INVOKABLE TreeElement *parentItem() const;
    bool insertItem(TreeElement *item, int pos = -1);
    QQmlListProperty<TreeElement> items();

    TreeElement *child(int index) const;
    void clear();

    Q_INVOKABLE int pos() const;
    Q_INVOKABLE int count() const;

private:
    static void appendElement(QQmlListProperty<TreeElement> *property, TreeElement *value);
    static int countElement(QQmlListProperty<TreeElement> *property);
    static void clearElement(QQmlListProperty<TreeElement> *property);
    static TreeElement *atElement(QQmlListProperty<TreeElement> *property, int index);

    QList<TreeElement *> m_childs;
    TreeElement *m_parent;
};
#endif // TreeElement_H

// treeelement.cpp
#include "treeelement.h"

TreeElement::TreeElement(QObject *parent) :
    QObject(parent),
    m_parent(nullptr) {}

TreeElement *TreeElement::parentItem() const{
    return m_parent;
}


QQmlListProperty<TreeElement> TreeElement::items(){
    return  QQmlListProperty<TreeElement> (this,
                                           this,
                                           &TreeElement::appendElement,
                                           &TreeElement::countElement,
                                           &TreeElement::atElement,
                                           &TreeElement::clearElement);
}

TreeElement *TreeElement::child(int index) const{
    if(index < 0 || index >= m_childs.length())
        return nullptr;
    return m_childs.at(index);
}

void TreeElement::clear(){
    qDeleteAll(m_childs);
    m_childs.clear();
}

bool TreeElement::insertItem(TreeElement *item, int pos){
    if(pos > m_childs.count())
        return false;
    if(pos < 0)
        pos = m_childs.count();
    item->m_parent = this;
    item->setParent(this);
    m_childs.insert(pos, item);
    return true;
}

int TreeElement::pos() const{
    TreeElement *parent = parentItem();
    if(parent)
        return parent->m_childs.indexOf(const_cast<TreeElement *>(this));
    return 0;
}

int TreeElement::count() const{
    return m_childs.size();
}

void TreeElement::appendElement(QQmlListProperty<TreeElement> *property, TreeElement *value){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    parent->insertItem(value);
}

int TreeElement::countElement(QQmlListProperty<TreeElement> *property){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    return parent->count();
}

void TreeElement::clearElement(QQmlListProperty<TreeElement> *property){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    parent->clear();
}

TreeElement *TreeElement::atElement(QQmlListProperty<TreeElement> *property, int index){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    if(index < 0 || index >= parent->count())
        return nullptr;
    return parent->child(index);
}

树模型

// treemodel.h
#ifndef TreeModel_H
#define TreeModel_H

#include <QAbstractItemModel>
#include <QQmlListProperty>

class TreeElement;

class TreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    Q_PROPERTY(QQmlListProperty<TreeElement> items READ items)
    Q_PROPERTY(QVariantList roles READ roles WRITE setRoles NOTIFY rolesChanged)
    Q_CLASSINFO("DefaultProperty", "items")

    TreeModel(QObject *parent = Q_NULLPTR);
    ~TreeModel() override;

    QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
    QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
    Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QQmlListProperty<TreeElement> items();

    QVariantList roles() const;
    void setRoles(const QVariantList &roles);

    Q_INVOKABLE QModelIndex indexFromElement(TreeElement *item);
    Q_INVOKABLE bool insertElement(TreeElement *item, const QModelIndex &parent = QModelIndex(), int pos = -1);

    TreeElement *elementFromIndex(const QModelIndex &index) const;

private:
    TreeElement *m_root;
    QHash<int, QByteArray> m_roles;

signals:
    void rolesChanged();
};

#endif // TreeModel_H

// treemodel.cpp
#include "treemodel.h"
#include "treeelement.h"

TreeModel::TreeModel(QObject *parent) :
    QAbstractItemModel(parent){
    m_root = new TreeElement;
}
TreeModel::~TreeModel(){
    delete m_root;
}

QHash<int, QByteArray> TreeModel::roleNames() const{
    return m_roles;
}

QVariant TreeModel::data(const QModelIndex &index, int role) const{
    if (!index.isValid())
        return QVariant();
    TreeElement *item = static_cast<TreeElement*>(index.internalPointer());
    QByteArray roleName = m_roles[role];
    QVariant name = item->property(roleName.data());
    return name;
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const{
    if (!index.isValid())
        return nullptr;
    return QAbstractItemModel::flags(index);
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
    TreeElement *parentItem = elementFromIndex(parent);
    TreeElement *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex &index) const{
    if (!index.isValid())
        return QModelIndex();
    TreeElement *childItem = static_cast<TreeElement*>(index.internalPointer());
    TreeElement *parentItem = static_cast<TreeElement *>(childItem->parentItem());
    if (parentItem == m_root)
        return QModelIndex();
    return createIndex(parentItem->pos(), 0, parentItem);
}

int TreeModel::rowCount(const QModelIndex &parent) const{
    if (parent.column() > 0)
        return 0;
    TreeElement *parentItem = elementFromIndex(parent);
    return parentItem->count();
}

int TreeModel::columnCount(const QModelIndex &parent) const{
    Q_UNUSED(parent)
    return 1;
}

QQmlListProperty<TreeElement> TreeModel::items(){
    return m_root->items();
}

QVariantList TreeModel::roles() const{
    QVariantList list;
    QHashIterator<int, QByteArray> i(m_roles);
    while (i.hasNext()) {
        i.next();
        list.append(i.value());
    }

    return list;
}

void TreeModel::setRoles(const QVariantList &roles){
    static int nextRole = Qt::UserRole + 1;
    for(QVariant role : roles) {
        m_roles.insert(nextRole, role.toByteArray());
        nextRole ++;
    }
    emit rolesChanged();
}

QModelIndex TreeModel::indexFromElement(TreeElement *item){
    QVector<int> positions;
    QModelIndex result;
    if(item) {
        do{
            int pos = item->pos();
            positions.append(pos);
            item = item->parentItem();
        } while(item != nullptr);

        for (int i = positions.size() - 2; i >= 0 ; i--)
            result = index(positions[i], 0, result);
    }
    return result;
}


bool TreeModel::insertElement(TreeElement *item, const QModelIndex &parent, int pos){
    TreeElement *parentElement = elementFromIndex(parent);
    if(pos >= parentElement->count())
        return false;
    if(pos < 0)
        pos = parentElement->count();
    beginInsertRows(parent, pos, pos);
    bool retValue = parentElement->insertItem(item, pos);
    endInsertRows();
    return retValue;
}

TreeElement *TreeModel::elementFromIndex(const QModelIndex &index) const{
    if(index.isValid())
        return static_cast<TreeElement *>(index.internalPointer());
    return m_root;
}

main.cpp

#include "treemodel.h"
#include "treeelement.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>

static void registertypes(){
    qmlRegisterType<TreeElement>("foo", 1, 0, "TreeElement");
    qmlRegisterType<TreeModel>("foo", 1, 0, "TreeModel");
}

Q_COREAPP_STARTUP_FUNCTION(registertypes)

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);    
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    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();
}
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 1.4

import foo 1.0

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

    TreeModel {
        id: treemodel
        roles: ["phrase"]
        TreeElement{
            property string phrase: "Hey"
            TreeElement{
                property string phrase: "What's"
                TreeElement{
                    property string phrase: "Up?"
                }
            }
        }
    }
    TreeView {
        anchors.fill: parent
        model: treemodel
        TableViewColumn {
            title: "Name"
            role: "phrase"
            width: 200
        }
    }
}

Output:

enter image description here

The complete example you find here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这是 QML 中的最小可行 TreeView 模型吗? 的相关文章

随机推荐