当前位置: 代码迷 >> 综合 >> Qt系列文章之 QAbstractItemModel(中)
  详细解决方案

Qt系列文章之 QAbstractItemModel(中)

热度:91   发布时间:2024-02-24 23:34:57.0

上一篇文章基于QAbstractItemModel新建了一个用户模型类,此文紧接上一篇文章来对用户模型进行具体设计和实现。

QAbstractItemModel

生成的用户模型给出了模型的标准接口;但是任何模型都是对数据封装,那么必然需要专用对象来储存数据,而这个储存数据的对象就具有十分多的形式,可以是Qt/C++容器、数组、字符串、文件、SQL对象等。而用户类就是将用户各种形式的数据储存方式转为统一的模型接口以供其他类(如视图类)来使用,同时将其他类的操作转化到对具体数据对象的具体操作(如写、添加、删除等)。

此处为了简化接口,构造一个常见的Table类用户模型,模型一共两列,每行第一列储存“Name“格式是QString,每行第二列储存“Age”格式是int。采用QMap<QString,int>来储存每一行的数据,采用QVector<QPair<QString,int>>来储存所有行的数据。在h文件中私有属性定义一个数据储存指针:
QVector<QPair<QString,int>> *m_dataVector = nullptr; //新建一个储存数据的序列对象,每个元素(行)两个值,一个储存姓名、一个储存年龄

同时定义一个QList m_headName来储存表头名称。
在cpp构造函数中对数据储存指针进行初始化并给数据列表初始化一个默认值:

CustomItemModel::CustomItemModel(QObject *parent): QAbstractItemModel(parent),m_dataVector(new QVector<QPair<QString,int>>{
    })
{
    m_dataVector->push_back({
    "Bob",30});m_headName = {
    "Name","Age"};

在析构函数中对数据列表进行析构:

CustomItemModel::~CustomItemModel()
{
    if(m_dataVector) {
    m_dataVector->clear();delete m_dataVector;m_dataVector=nullptr;}
}

构造好数据储存形式之后,下一步就可以对用户模型的虚函数进行实现。这些虚函数的实现与用户自定义的数据结构和需求精密相关,没有具体实现方式可以参考,需根据具体场景来编写实现代码。

  • headerData(int section, Qt::Orientation orientation, int role)

获取表头信息,orientation表示表头方向,分为水平和垂直;section表示表头位置,如果是行,表示第几列;如果是列,表示第几行。Role表示表头角色信息。Model模型每个元素提供的role主要与如下一些。
在这里插入图片描述

此处我们只关心行表头,同时也只希望返回表头的显示信息即DisplayRole的值。一个简单的表头信息获取实现代码如下:

QVariant CustomItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role!=Qt::DisplayRole)return QVariant();if(orientation==Qt::Vertical)return QVariant(section);if(orientation==Qt::Horizontal){
    if(section==0) return QVariant(m_headName[0]);if(section==1) return QVariant(m_headName[1]);}return QVariant();
}
  • setHeaderData()

该函数实现对表头文件信息的修改,和上一个函数正好构成一对函数。同样,我们此处也只是暂时修改一下行表头的信息。

bool CustomItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
    if (value != headerData(section, orientation, role)) {
    if(role!=Qt::EditRole) return false;if(orientation==Qt::Vertical) return false;if(section==0) m_headName[0]= value.toString();if(section==1) m_headName[1]=value.toString();emit headerDataChanged(orientation, section, section);return true;}return false;
}
  • index(int row, int column, const QModelIndex &parent)

index根据表格中的父节点parent下的某行row和某列column信息获取对应的QModelIndex对象信息。如果行和列超出范围,返回一个无效的QModelIndex()。

QModelIndex CustomItemModel::index(int row, int column, const QModelIndex &parent) const
{
    if(row>m_dataVector->size()-1 || row < 0) return  QModelIndex();if(column>1 || column<0) return QModelIndex();return createIndex(row,column);
}
  • parent(const QModelIndex &index)

函数实现获取某个index的父节点,由于此处是表格模型,默认认为父节点是无效空节点。

QModelIndex CustomItemModel::parent(const QModelIndex &index) const
{
    // FIXME: Implement me!return QModelIndex();
}
  • rowCount(const QModelIndex &parent)

获取某个父节点parent下的模型行数,直接范围数据列表的size即可:

int CustomItemModel::rowCount(const QModelIndex &parent) const
{
    return m_dataVector->size();
}
  • columnCount(const QModelIndex &parent)

返回某个父节点下的模型列数,此处直接返回2:

int CustomItemModel::columnCount(const QModelIndex &parent) const
{
    return 2;
}
  • hasChildren(const QModelIndex &parent)

判断某个节点是否还有子节点,这种判断对于树形模型来说十分重要,表格模型默认都没有子节点:

bool CustomItemModel::hasChildren(const QModelIndex &parent) const
{
    return false;
}
  • canFetchMore(const QModelIndex &parent)

判断数据是否还有更多数据节点可以获取:

bool CustomItemModel::canFetchMore(const QModelIndex &parent) const
{
    return false;
}
  • fetchMore(const QModelIndex &parent)

由于没有更多数据,此处不做额外处理:

void CustomItemModel::fetchMore(const QModelIndex &parent)
{
    // FIXME: Implement me!
}
  • data(const QModelIndex &index, int role)

data()和setData()函数是十分钟的对模型元素进行操作的函数。data()函数根据角色和索引号获取到对应的元素值。如下几个角色比较关键:
在这里插入图片描述

此处我们暂时只关心DisplayRole的值,该函数的实现代码如下,此处使用switch来判断不同角色对应的值,后面可以做一些比较全面的补充:

QVariant CustomItemModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())return QVariant();if(index.row()>m_dataVector->size()-1 || index.row()<0 || index.column()<0  ||index.column()>1)return QVariant();switch(role){
    case Qt::DisplayRole:{
    if(index.column()==0){
    QString name = m_dataVector->at(index.row()).first;return QVariant(name);}if(index.column()==1){
    int age = m_dataVector->at(index.row()).second;return QVariant(age);}break;}
  • setData(const QModelIndex &index, const QVariant &value, int role)

该函数将某个元素(index指定)某个角色role的值设置为value的值,此处关注对显示字符串的编辑角色,实现代码如下:

bool CustomItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (data(index, role) != value){
    switch (role){
    case Qt::EditRole:{
    QString name = m_dataVector->at(index.row()).first;int age = m_dataVector->at(index.row()).second;if(index.column()==0){
    QPair<QString,int> pair(value.toString(),age);m_dataVector->replace(index.row(),pair);}if(index.column()==1){
    QPair<QString,int> pair(name,value.toInt());m_dataVector->replace(index.row(),pair);}emit dataChanged(index, index, QVector<int>() << role);return true;}
  • flags(const QModelIndex &index)

获取某个元素(index指定的)的所有属性标志位,多个属性采用按位或“|”算法链接,此处我们先设置如下标志位属性。

Qt::ItemFlags CustomItemModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())return Qt::NoItemFlags;return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable |Qt::ItemIsUserCheckable; // FIXME: Implement me!
}

本文对几个关键的虚函数进行了实现,下一文将继续介绍剩下几个虚函数的实现,同时将模型集成到视图并显示出来。


欢迎同好沟通交流,批评指正,欢迎关注我的公号:不如起而行之