在Qt中,模型视图框架是许多应用程序的核心。这种框架使用模型来表示数据,使用视图(如QTableView或QTreeView)来将该数据可视化。Qt中提供了一些内置的模型类,但是,如果您需要为自己的应用程序定义自定义的数据源,那么您需要了解QAbstractItemModel的工作方式。
一、QAbstractItemModel简介
QAbstractItemModel是Qt的一个关键类,它为开发人员提供了一个接口,用于指定模型数据的组织方式。它是QAbstractTableModel和QAbstractListModel等模型的基类,这些模型提供了一些常见的实现,例如表格和列表。QAbstractItemModel用于QTreeView,QListView,QTableView等视图类的基类,它们都使用基于文件夹的数据模型来显示数据。
QAbstractItemModel所有直接的子类应该在以下一个函数中实现两个不同的索引:index()和parent()。这些函数定义了静态树形结构,元素的位置不会改变。这些函数能够让Qt高效地处理大型树形结构。index()和parent()会被Qt视图类用于将数据排列为TreeView,ListView和TableView等典型模型视图结构。
二、QAbstractItemModel的子类实现
1、自定义QAbstractItemModel
class CustomModel : public QAbstractItemModel { public: CustomModel(QObject *parent = 0); ~CustomModel(); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); };
上面是一个自定义的QAbstractItemModel的框架。在这个例子中,根节点是一个空的QModelIndex,它没有坐标以及没有父节点。可以看到,index()和parent()都需要返回一个QModelIndex,它们指定了行和列的位置。rowCount()和columnCount()函数返回模型中行的数量和列的数量。
在此例子中,data()和setData()函数的实现也是必不可少的。它们定义了数据将如何存储和访问。在大多数情况下,将使用QVariant来存储数据,而且在data()函数中使用role参数来返回适合给定视图类型的数据。setData()负责更新模型数据。
2、Qt自带的Model Index
QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const
index()函数返回一个QModelIndex对象,此对象由行号和列号组成,该索引属于parent。在许多情况下,一些QAbstractItemModel的派生类经常使用这些方法来创建一个自己的index()。
3、返回父级QModelIndex
QModelIndex QAbstractItemModel::parent(const QModelIndex &index) const
parent()函数返回一个QModelIndex对象,该对象传递回索引的父级项的位置。parent()和index()通常应该是相互的逆。parent()为顶级ItemModel或已知索引返回无效的QModelIndex。因此,QModelIndex的行号和列号都是0。
4、获取行数和列数
int QAbstractItemModel::rowCount(const QModelIndex &parent = QModelIndex()) const int QAbstractItemModel::columnCount(const QModelIndex &parent = QModelIndex()) const
这两个函数返回模型中给定父项的行数或列数。如果没有父项,则返回顶层项的行数或列数。row()和column()一样,都从0开始计数。在许多情况下,这些函数将用于初始化一个ItemView部件。
5、获取和设置ItemData
QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
data()函数返回与给定模型索引关联的数据。可以为给定角色请求数据。setData()函数将给定值存储在模型索引处。如果此处发生错误,则使用Rollback数据。
三、QAbstractItemModel的应用举例
1、创建树型结构
这个应用程序是一个树型表格。它的数据源是硬编码,但该例子很好地展示了如何扩展QAbstractItemModel来支持树型结构:
class TreeModel : public QAbstractItemModel { Q_OBJECT public: TreeModel(QObject *parent = 0); ~TreeModel(); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); private: TreeItem *rootItem; };
具体的实现过程就不再贴出,可以参考Qt自带例子,这里给出参考:
2、扩展现有的模型类
下面是一个QAbstractItemModel的派生类,该类从QAbstractListModel扩展:
class StringListModel : public QAbstractListModel { Q_OBJECT public: explicit StringListModel(const QStringList &strings, QObject *parent = nullptr) : QAbstractListModel(parent), stringList(strings) { } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if (parent.isValid()) return 0; return stringList.count(); } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid()) return QVariant(); if (index.row() >= stringList.size()) return QVariant(); if (role == Qt::DisplayRole) return stringList.at(index.row()); else return QVariant(); } private: QStringList stringList; };
StringListModel扩展了QAbstractListModel并实现了rowCount()和data()函数来处理一个QStringList中的内容。这是一个非常基本的示例,但当您需要单行列表模型时,这可能是一个很有用的例子。
3、处理排序和过滤
排序和过滤是QAbstractItemModel的高级应用。排序需要将数据按特定顺序排列,而过滤则需要以某种方式选择数据,例如将其筛选掉。这是一个基本示例:
class SortFilterModel : public QSortFilterProxyModel { public: SortFilterModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { } bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { QModelIndex index0 = sourceModel()->index(source_row, 0, source_parent); QRegExp regExp("^\\[0-9\\.]+(.*)"); QString str = index0.data(Qt::DisplayRole).toString(); if (regExp.indexIn(str) != -1) { QString capStr = regExp.cap(1); if (capStr < "30") return false; } return true; } };
在此示例中,过滤器使用QRegExp来查找所有标题是数字(例如,[20]Title 3)的行。如果标题中的数字小于30,则该行将被过滤掉。
四、总结
QAbstractItemModel为开发者提供了一个用于管理自定义数据的模型视图框架。开发者可以通过继承并实现QAbstractItemModel的相关函数来创建自己的模型,或者继承并扩展现有的模型类。此外,QAbstractItemModel还支持排序和过滤。学会使用QAbstractItemModel可以让应用程序在数据管理方面完美地运转。