自定义QAbstractItemModel派生类实现树形结构模型

科技   2024-12-09 11:38   上海  

在Qt框架中,QAbstractItemModel是一个强大的抽象基类,用于定义模型/视图(M/V)架构中的数据结构。为了构建一棵树的自定义模型,我们需要继承QAbstractItemModel并实现其纯虚函数。本文将详细分析如何创建一个支持树结构的自定义QAbstractItemModel派生类,并提供代码示例。

1. 继承QAbstractItemModel

首先,我们创建一个新的类,继承自QAbstractItemModel。这个类将实现所有必要的函数,以支持树形结构的数据。

#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QList>
#include <QDebug>

class TreeNode {
public:
    QString data;
    QList<TreeNode*> children;

    TreeNode(const QString& data) : data(data) {}
    ~TreeNode() {
        qDeleteAll(children);
    }
};

class TreeModel : public QAbstractItemModel {
    Q_OBJECT

public:
    TreeModel(QObject* parent = nullptr) : QAbstractItemModel(parent), root(new TreeNode("Root")) {}
    ~TreeModel() { delete root; }

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    Qt::ItemFlags flags(const QModelIndex& index) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex& index) const override;
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    int columnCount(const QModelIndex& parent = QModelIndex()) const override;

    void addChild(const QModelIndex& parent, const QString& data);

private:
    TreeNode* root;
    TreeNode* nodeFromIndex(const QModelIndex& index) const;
};

2. 实现必要函数

接下来,我们实现QAbstractItemModel中的必要函数,以支持树形结构。

QVariant TreeModel::data(const QModelIndex& index, int role) const {
    if (!index.isValid()) return QVariant();

    TreeNode* node = nodeFromIndex(index);

    if (role == Qt::DisplayRole) {
        return node->data;
    }

    return QVariant();
}

Qt::ItemFlags TreeModel::flags(const QModelIndex& index) const {
    if (!index.isValid()) return Qt::NoItemFlags;

    return QAbstractItemModel::flags(index) | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}

QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        return QString("Column %1").arg(section + 1);
    }
    return QVariant();
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex& parent) const {
    if (!hasIndex(row, column, parent)) return QModelIndex();

    TreeNode* parentNode = nodeFromIndex(parent);

    TreeNode* childNode = parentNode->children.value(row);
    if (childNode) {
        return createIndex(row, column, childNode);
    }

    return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex& index) const {
    if (!index.isValid()) return QModelIndex();

    TreeNode* childNode = nodeFromIndex(index);
    TreeNode* parentNode = childNode->parent();

    if (parentNode == root) return QModelIndex();

    TreeNode* grandParentNode = parentNode->parent();
    if (grandParentNode) {
        int row = grandParentNode->children.indexOf(parentNode);
        return createIndex(row, 0, parentNode);
    }

    return QModelIndex();
}

int TreeModel::rowCount(const QModelIndex& parent) const {
    TreeNode* parentNode = nodeFromIndex(parent);
    return parentNode->children.count();
}

int TreeModel::columnCount(const QModelIndex& parent) const {
    return 1// 假设树只有一列
}

void TreeModel::addChild(const QModelIndex& parent, const QString& data) {
    beginInsertRows(parent, rowCount(parent), rowCount(parent));

    TreeNode* parentNode = nodeFromIndex(parent);
    parentNode->children.append(new TreeNode(data));

    endInsertRows();
}

TreeNode* TreeModel::nodeFromIndex(const QModelIndex& index) const {
    if (!index.isValid()) return root;

    TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
    return node;
}

3. 使用自定义模型

现在,我们可以使用自定义的TreeModel类来创建和显示树形结构。

#include <QApplication>
#include <QTreeView>

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);

    TreeModel model;
    QTreeView view;
    view.setModel(&model);

    QModelIndex rootIndex = model.index(00);
    model.addChild(rootIndex, "Child 1");
    model.addChild(rootIndex, "Child 2");
    model.addChild(model.index(00, rootIndex), "Grandchild 1");

    view.show();
    return app.exec();
}

4. 注意事项

  • 在实现nodeFromIndex函数时,我们使用index.internalPointer()来获取与QModelIndex关联的TreeNode指针。这是QAbstractItemModel中常用的技巧,用于在索引和底层数据之间建立联系。
  • 在添加子节点时,我们使用beginInsertRowsendInsertRows信号来通知视图模型结构的变化。这是确保视图正确更新数据的关键步骤。
  • 在析构函数中,我们确保删除所有子节点,以避免内存泄漏。

通过上述步骤,我们成功创建了一个支持树形结构的自定义QAbstractItemModel派生类。这个类可以轻松地与Qt的视图组件(如QTreeView)集成,以显示和操作树形数据。


Qt教程
致力于Qt教程,Qt技术交流,研发
 最新文章