I made similar solution. There is my model:
Tree Item
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;
}
Model
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);
}
}
}
If you need insert, remove, move functions, you must implement this function.
Complite program with draggable rows you can download here.
TreeView
is depends on a model the right and the only way is to define your custom model, certainly derived from QAbstractItemModel. Therefore, your model should be dynamic ie this should allow data to be changed on the fly, if I understood correctly. In that case, you should look atinsertRows
/removeRows
etc. – folibisQAbstractItemModel
subclass will do. Or even a regularQStandardItemModel
as long as it fits your needs. Both support tree models. – dtech