这是 QML 中最小可行的 TreeView 模型吗? [英] Is this the Minimum Viable TreeView Model in QML?
问题描述
我正在制作一个包含三个项目的折叠列表:嘿"、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:
- QML Treeview
- QML QAbstractItemModel
- This Question by My_Cat_Jessica
- This question by kavaliero which was based on:
- This 'working' example by Qt themselves (Doesn't actually work for TreeView. Works for QTreeView though!)
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屋!