QML 创建 TreeView 动态模型的正确方法是啥?
Posted
技术标签:
【中文标题】QML 创建 TreeView 动态模型的正确方法是啥?【英文标题】:QML what is the right way to create a TreeView dynamic model?QML 创建 TreeView 动态模型的正确方法是什么? 【发布时间】:2018-06-15 14:36:48 【问题描述】:我是 QML 的新手,最近在集成 QML 和 C++ 时遇到了一些麻烦,现在我尝试正确地进行集成。
所以,我正在尝试使用动态模型创建 QML TreeView,并且我看到了创建 TreeView 模型的不同方法。
在文档中,示例与 TableViewColumn:
https://doc-snapshots.qt.io/qt5-5.9/qml-qtquick-controls-treeview.html
在互联网上的一些研究中,我发现: 用 C++ 创建:
https://forum.qt.io/topic/56497/request-treeview-c-model-to-qml-example/4
Create Model for QML TreeView
那么,我的问题是,为 QML TreeView 创建模型的正确方法是什么? 该模型将是动态的,具有动态数据。
对于动态,我的意思是会有不固定数量的节点,但是信息是一样的,它遵循一个 json 和一个示例图像:
[
"description": "screen1",
"source": "qrc/screen1.qml",
"popups":
[
"description": "screen1popup1",
"source": "qrc/screen1popup1.qml"
,
"description": "screen1popup2",
"source": "qrc/screen1popup2.qml"
]
,
"description": "screen2",
"source": "qrc/screen2.qml",
"popups":
[
"description": "screen2popup1",
"source": "qrc/screen2popup1.qml",
"subs": [
"description": "screen2popup1sub1",
"source": "qrc/screen2popup1sub1.qml"
]
]
,
"description": "screen3",
"source": "qrc/screen3.qml"
]
【问题讨论】:
您可以解释您的意思 * 该模型将是动态的,具有动态数据。 *,这个表达式很宽泛,可能无法回答,也许如果你建立一些真实的参数,我们可以给你一个实际的例子。 @eyllanesc 好的,我会的 由于TreeView
是依赖于模型的权利,唯一的方法是定义您的自定义模型,当然从QAbstractItemModel 派生。因此,您的模型应该是动态的,即如果我理解正确的话,这应该允许动态更改数据。在这种情况下,您应该查看insertRows
/removeRows
等。
一个QAbstractItemModel
子类就可以了。甚至是普通的QStandardItemModel
,只要它符合您的需要。两者都支持树模型。
【参考方案1】:
我做了类似的解决方案。有我的模型:
树项treeitem.h
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QList>
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonObject>
#include <QJsonDocument>
class TreeItem
public:
explicit TreeItem(const QJsonValue &data, const QString childrenPath, TreeItem *parentItem = 0);
~TreeItem() qDeleteAll(m_childItems);
bool insertChild(int i, TreeItem *child);
TreeItem *child(int row) return m_childItems.at(row);
int childCount() const return m_childItems.count();
int columnCount() const return m_itemData.toObject().count();
QVariant data(const QString &roleName) const;
bool setParentItem(TreeItem *item);
int row() const;
TreeItem *parentItem() return m_parentItem;
QJsonValue jsonValue() const;
bool removeChild(int row, int count);
bool isTheIdExist(QString id);
bool isParent(TreeItem *item);
private:
QVector<TreeItem*> m_childItems;
QJsonValue m_itemData;
TreeItem *m_parentItem;
QString childrenPath;
;
#endif // TREEITEM_H
treeitem.cpp
#include <QDateTime>
#include "treeitem.h"
TreeItem::TreeItem(const QJsonValue &data, const QString childrenPath, TreeItem *parentItem) :
m_parentItem(parentItem), childrenPath(childrenPath)
QJsonObject jObject = data.toObject();
jObject.remove(childrenPath);
m_itemData = QJsonValue(jObject);
bool TreeItem::insertChild(int i, TreeItem *child)
if (i >= m_childItems.count())
m_childItems.append(child);
else
m_childItems.insert(i, child);
return true;
QVariant TreeItem::data(const QString &roleName) const
QJsonValue val = m_itemData.toObject().value(roleName);
if (val.type() == QJsonValue::String)
QString strVal = val.toString();
QDateTime dtVal = QDateTime::fromString(strVal, Qt::ISODate);
if (dtVal.isValid())
return dtVal;
else if (val.type() == QJsonValue::Double)
int intVal = val.toInt(0);
if (intVal != 0)
return intVal;
else if (val.type() == QJsonValue::Bool)
return val.toBool();
return val.toVariant();
bool TreeItem::setParentItem(TreeItem *item)
if (item == this)
return false;
foreach (TreeItem *i, m_childItems)
if (item->isParent(i))
return false;
m_parentItem = item;
return true;
int TreeItem::row() const
if (m_parentItem)
return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
QJsonValue TreeItem::jsonValue() const
QJsonObject jObj = m_itemData.toObject();
QJsonArray jArray;
if (m_childItems.count() > 0)
foreach (TreeItem *i, m_childItems)
jArray.append(i->jsonValue());
jObj.insert(childrenPath, jArray);
if (m_itemData.toObject().empty())
return QJsonValue(jArray);
else
return QJsonValue(jObj);
bool TreeItem::removeChild(int row, int count)
if (row > -1 && row+count <= m_childItems.count())
for (int i = count; i > 0; i--)
m_childItems.removeAt(row + i - 1);
return true;
return false;
bool TreeItem::isTheIdExist(QString id)
if (m_itemData.toObject().value("id").toInt() == id.toInt())
return true;
foreach (TreeItem *item, m_childItems)
if (item->isTheIdExist(id))
return true;
return false;
bool TreeItem::isParent(TreeItem *item)
bool result = false;
if (parentItem() != Q_NULLPTR)
if (parentItem()->isParent(item)) result = true;
if (parentItem() == item) result = true;
return result;
模型treejsonmodel.h
#ifndef TREEJSONMODEL_H
#define TREEJSONMODEL_H
#include <QAbstractItemModel>
#include <QFile>
#include <QJSValue>
#include <QDebug>
#include "treeitem.h"
class TreeJsonModel : public QAbstractItemModel
Q_OBJECT
public:
explicit TreeJsonModel(QObject *parent = 0);
~TreeJsonModel();
Q_PROPERTY(bool hasChanges READ hasChanges NOTIFY hasChangesChanged)
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
Q_INVOKABLE int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &) const Q_DECL_OVERRIDE return _columns.count();
bool hasChildren(const QModelIndex &parent) const Q_DECL_OVERRIDE;
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column, const QModelIndex &parent) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE;
void registerColumn(const QString &name);
void setUrl(const QString &url) _fileUrl = url;
bool hasChanges() const return _hasChanges;
Q_INVOKABLE bool submit() Q_DECL_OVERRIDE;
Q_INVOKABLE void refresh();
signals:
void dataReady();
void hasChangesChanged();
private:
QString _fileUrl;
QString _childrenPath = "parents"; // this is the name of path with children
QStringList _columns;
TreeItem *rootItem = Q_NULLPTR;
bool _hasChanges = false;
void addNewItem(const QJsonValue &data, TreeItem *parent = nullptr);
void addNewItem(const QJsonValue &data, int row, TreeItem *parent = nullptr);
;
#endif // TREEJSONMODEL_H
*treejsonmodel.cpp*
#include "treejsonmodel.h"
TreeJsonModel::TreeJsonModel(QObject *parent) :
QAbstractItemModel(parent)
TreeJsonModel::~TreeJsonModel()
submit();
delete(rootItem);
Qt::ItemFlags TreeJsonModel::flags(const QModelIndex &index) const
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
QVariant TreeJsonModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->data(roleNames().value(role));
QVariant TreeJsonModel::headerData(int section, Qt::Orientation orientation, int role) const
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(roleNames().value(section));
return QVariant();
int TreeJsonModel::rowCount(const QModelIndex &parent) const
TreeItem *parentItem;
if (parent.column() > 0 || rootItem == Q_NULLPTR)
return 0;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
return parentItem->childCount();
bool TreeJsonModel::hasChildren(const QModelIndex &parent) const
return rowCount(parent) > 0;
QHash<int, QByteArray> TreeJsonModel::roleNames() const
QHash<int, QByteArray> result = QAbstractItemModel::roleNames();
for (int i = 0; i < _columns.count(); i++)
int id = Qt::UserRole + 1 + i;
QByteArray byte = _columns.at(i).toUtf8();
result.insert(id, byte);
return result;
QModelIndex TreeJsonModel::index(int row, int column, const QModelIndex &parent) const
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
QModelIndex TreeJsonModel::parent(const QModelIndex &index) const
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parentItem();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
void TreeJsonModel::registerColumn(const QString &name)
if (!_columns.contains(name))
_columns.append(name);
void TreeJsonModel::refresh()
QFile file(_fileUrl);
if (!file.open(QIODevice::ReadOnly))
qDebug() << "Can't open file" << _fileUrl;
return;
beginResetModel();
QJsonDocument jDoc = QJsonDocument::fromJson(file.readAll());
rootItem = new TreeItem(QJsonValue(), _childrenPath);
foreach (QJsonValue item, jDoc.array())
addNewItem(item, rootItem);
_hasChanges = false;
hasChangesChanged();
emit endResetModel();
emit dataReady();
file.close();
bool TreeJsonModel::submit()
QFile file(_fileUrl);
if (!file.open(QIODevice::WriteOnly))
qDebug() << "Can't open file";
return false;
QJsonValue jValue = rootItem->jsonValue();
QJsonDocument jDoc = QJsonDocument(jValue.toArray());
file.write(jDoc.toJson());
file.close();
_hasChanges = false;
hasChangesChanged();
emit dataReady();
return true;
void TreeJsonModel::addNewItem(const QJsonValue &data, TreeItem *parent)
addNewItem(data, parent->childCount(), parent);
void TreeJsonModel::addNewItem(const QJsonValue &data, int row, TreeItem *parent)
auto *item = new TreeItem(data, _childrenPath, parent);
parent->insertChild(row, item);
QJsonValue v = data.toObject().value(_childrenPath);
if (v != QJsonValue::Undefined && v.isArray())
foreach (QJsonValue val, v.toArray())
addNewItem(val, item);
如果你需要插入、删除、移动功能,你必须实现这个功能。 带有可拖动行的完整程序,您可以下载here。
【讨论】:
以上是关于QML 创建 TreeView 动态模型的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章