在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(0, 0);
model.addChild(rootIndex, "Child 1");
model.addChild(rootIndex, "Child 2");
model.addChild(model.index(0, 0, rootIndex), "Grandchild 1");
view.show();
return app.exec();
}
4. 注意事项
在实现 nodeFromIndex
函数时,我们使用index.internalPointer()
来获取与QModelIndex关联的TreeNode指针。这是QAbstractItemModel中常用的技巧,用于在索引和底层数据之间建立联系。在添加子节点时,我们使用 beginInsertRows
和endInsertRows
信号来通知视图模型结构的变化。这是确保视图正确更新数据的关键步骤。在析构函数中,我们确保删除所有子节点,以避免内存泄漏。
通过上述步骤,我们成功创建了一个支持树形结构的自定义QAbstractItemModel派生类。这个类可以轻松地与Qt的视图组件(如QTreeView)集成,以显示和操作树形数据。