在 Qt 的 Model-View-Controller (MVC) 框架中,数据模型决定了数据的组织和呈现方式。以下是三种常用的模型基类及其适用场景的具体理解:


1. 列表模型:QAbstractListModel

特点
  • 用于一维数据(单列、多行)的简单表示。
  • 适合表示列表形式的数据,如任务列表、菜单选项等。
结构
  • 数据是线性排列的,每一项对应一个索引(row),但只有一列(column 始终为 0)。
常见视图
  • QListView:最常见的列表视图。
  • QComboBox:下拉框。
实现时需要重写的方法
  • rowCount():返回列表的总行数。
  • data():提供每一行的数据。
  • 可选:
    • setData():支持数据编辑。
    • flags():定义是否可编辑或选中。
示例
class StringListModel : public QAbstractListModel {
    Q_OBJECT

public:
    explicit StringListModel(const QStringList &strings, QObject *parent = nullptr)
        : QAbstractListModel(parent), m_data(strings) {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override {
        return parent.isValid() ? 0 : m_data.size();
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (!index.isValid() || index.row() >= m_data.size()) return QVariant();
        if (role == Qt::DisplayRole) return m_data.at(index.row());
        return QVariant();
    }

private:
    QStringList m_data;
};

2. 表格模型:QAbstractTableModel

特点
  • 用于二维数据(多行、多列)。
  • 适合表示表格形式的数据,如电子表格、数据库表、棋盘等。
结构
  • 数据由行和列组成,可以通过行号(row)和列号(column)进行索引。
  • 每个单元格都有独立的数据。
常见视图
  • QTableView:标准表格视图。
实现时需要重写的方法
  • rowCount():返回表格的行数。
  • columnCount():返回表格的列数。
  • data():提供每个单元格的数据。
  • 可选:
    • setData():支持单元格编辑。
    • headerData():提供表头信息。
    • flags():定义单元格的行为(如是否可选中、可编辑等)。
示例
class TableModel : public QAbstractTableModel {
    Q_OBJECT

public:
    explicit TableModel(const QVector<QVector<QString>> &data, QObject *parent = nullptr)
        : QAbstractTableModel(parent), m_data(data) {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override {
        return parent.isValid() ? 0 : m_data.size();
    }

    int columnCount(const QModelIndex &parent = QModelIndex()) const override {
        return parent.isValid() ? 0 : (m_data.isEmpty() ? 0 : m_data[0].size());
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (!index.isValid() || index.row() >= m_data.size() || index.column() >= m_data[0].size())
            return QVariant();
        if (role == Qt::DisplayRole) return m_data[index.row()][index.column()];
        return QVariant();
    }

private:
    QVector<QVector<QString>> m_data;
};

3. 树模型或复杂数据结构:QAbstractItemModel

特点
  • 用于表示多层次数据(树形结构)。
  • 适合表示目录树、文件系统、组织结构等。
结构
  • 数据节点可以有父子关系,每个节点可以拥有多个子节点。
  • 使用 QModelIndex 标识节点的位置。
常见视图
  • QTreeView:标准树形视图。
实现时需要重写的方法
  • rowCount():返回某节点下的子节点数。
  • columnCount():返回节点的列数。
  • data():提供节点的数据。
  • parent():返回某节点的父节点。
  • index():返回某节点的索引。
实现树模型的复杂性
  • 与列表或表格不同,树模型需要管理每个节点的父子关系。
  • 必须实现 parent() 和 index() 方法来正确描述层次结构。
示例
class TreeModel : public QAbstractItemModel {
    Q_OBJECT

public:
    explicit TreeModel(QObject *parent = nullptr)
        : QAbstractItemModel(parent), rootNode(new TreeNode("Root")) {}

    ~TreeModel() { delete rootNode; }

    int rowCount(const QModelIndex &parent = QModelIndex()) const override {
        TreeNode *parentNode = getNode(parent);
        return parentNode->childCount();
    }

    int columnCount(const QModelIndex &parent = QModelIndex()) const override {
        return 1; // 单列树模型
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (!index.isValid()) return QVariant();
        TreeNode *node = getNode(index);
        if (role == Qt::DisplayRole) return node->data();
        return QVariant();
    }

    QModelIndex parent(const QModelIndex &child) const override {
        if (!child.isValid()) return QModelIndex();
        TreeNode *childNode = getNode(child);
        TreeNode *parentNode = childNode->parent();
        if (parentNode == rootNode || !parentNode) return QModelIndex();
        return createIndex(parentNode->row(), 0, parentNode);
    }

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override {
        TreeNode *parentNode = getNode(parent);
        TreeNode *childNode = parentNode->child(row);
        if (childNode) return createIndex(row, column, childNode);
        return QModelIndex();
    }

private:
    TreeNode *rootNode;

    TreeNode *getNode(const QModelIndex &index) const {
        if (index.isValid()) return static_cast<TreeNode *>(index.internalPointer());
        return rootNode;
    }
};

总结对比

模型类型基类适用场景常见视图数据结构
列表模型QAbstractListModel一维数据QListView单列、多行
表格模型QAbstractTableModel二维数据(多行多列)QTableView多行、多列
树模型或复杂数据结构QAbstractItemModel层次化或复杂结构QTreeView父子关系的多层次结构

通过选择合适的基类,开发者可以根据数据结构的复杂程度和需求来实现高效的自定义数据模型。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐