QML创建TreeView动态模型的正确方法是什么?

时间:2018-06-15 14:36:49

标签: qt treeview qml

我是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"  
  }
]

treeviewExample

1 个答案:

答案 0 :(得分: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);
        }
    }
}

如果需要插入,移除,移动功能,则必须实现此功能 具有可拖动行的Complite程序,您可以下载here