这是 QML 中最小可行的 TreeView 模型吗? [英] Is this the Minimum Viable TreeView Model in QML?

查看:12
本文介绍了这是 QML 中最小可行的 TreeView 模型吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个包含三个项目的折叠列表:嘿"、Whats"和Up?".我想把它放到树视图中.我知道这个列表将只包含这三个项目.因此,我想知道如何将这些项目嵌套"在一起.

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

  • <子>您可以在这里找到完整的示例

    I'm making a folding list of three items: "Hey", "Whats", and "Up?". I want to put it into a tree view. I know this list will only ever contain these three items. Therefore, I would like to know how to "nest" these items together.

    I know there are implementation for agile systems that support adding and removing parent/child objects, finding indexes... powerful models. However, I literally only need to display these items in an expandable/collapsable view. Here is what I've read through relating to C++ and QAbstractItemModels:

    Here is the simplest viable code to implement a treeview with model:

    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?"; }
        }
    }
    

    I would like for the output to result in a nested stack like this:

    Hey
        What's
            Up?
    

    But I am getting everything in a single column all aligned with each other:

    Hey
    What's
    Up?
    

    I know I haven't assigned parents, and I'm not entirely sure how to do that - But I'm not even sure if that's what needs done to this code. So my question is this: What's the final step missing to stack these three elements into an expandable/collapsible view?

    解决方案

    There is no native QML model that can use the TreeView, so I have implemented a model that tries to be generic:

    TreeElement

    // 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

    // 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:

    The complete example you find here

    这篇关于这是 QML 中最小可行的 TreeView 模型吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆