Qt学习笔记-----Model/View架构之自定义Model

Model/View Framework中提供了模型model的抽象基类QAbstractItemModel, 如果需要自定义模型就需要继承这个类并且实现一些必要的函数。
此外,Qt中又提供了QAbstractTableModel和QAbstractListModel分别继承于上述基类,由名字可以清楚的知道这两个类分别适用于表格模型和列表模型。对于这两个模型来说,很多函数已经重新实现过了,使用时直接继承即可。

QAbstractItemModel
QAbstractItemModel为元素模型类提供了抽象接口,自定义模型时需要继承这个类。自定义的模型分为只读模型和可编辑模型。

只读模型:内部数据不能修改
为了实现一个只读模型,需要重新实现以下函数:

flags

Qt::ItemFlags QAbstractItemModel::flags(const QModelIndex &index) const

这个函数返回被给模型索引index的标志,用于其他组件获得每一个元素的信息。在很多模型中,返回的flags应该包括Qt::ItemIsEnabled和Qt::ItemIsSelectable,表示模型中的元素是可以被访问和选择的。通常实现的形式为:

if(!index.isValid())
    return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable;

data

QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const

这个函数返回模型索引index的底层数据(一个模型索引包括某一元素的信息,包括行,列,以及数据),用于视图和委托访问数据。通常的实现形式如下:

if(!index.isValid())
    return QVariant();
if(index.row() >= /*数据总个数*/)
    return QVariant();
if(role == Qt::DisplayRole)
{
    int row = index.row();
    int column = index.column();
    //根据模型存储数据所用的数据结构来返回对应行和列的数据
    return /*数据*/
}
return QVariant();

headerData

QVariant QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role == Qt::DisplayRole) const

这个函数返回某部分对应方向上的表头,为Views提供显示在Views顶部(即最上方和最左边)的标识。通常实现形式为:

if(role != Qt::DisplayRole)
    return QVariant();
if(orientation == Qt::Horizontal)//headersData在最上方,水平方向
或if(orientation == Qt::Vertical) //headersData在最左边,垂直方向
{
    //判断section,section表示的是第几个
    return ...
}
return QVariant();

rowCount

int QAbstractItemModel::rowCount(const QModelIndex &parent = QModelIndex()) const

这个函数返回被给的模型索引下有多少行,返回的是parent的孩子数。而不是整个行数。如果没有子元素,则返回0。

对于继承于QAbstractListModel的类来说,只需要重新实现这四个函数。而对于继承于QAbstractTableModel和QAbstractItemModel的类来说,除了上述四个函数,还需要重新实现columnCount函数,因为列表只有1列,而表格和树则需要自定义:

columnCount

int QAbstractItemModel::columnCount(const QModelIndex &parent = QModelIndex()) const

这个函数通常与给定的parent无关,所涉及的类有几列就返回几。

可编辑模型:允许数据被修改,也可以允许插入和删除操作。

实现可编辑模型,除了上述只读模型的函数外,还需要重新实现以下函数:

flags

Qt::ItemFlags QAbstractItemModel::flags(const QModelIndex &index) const

这个函数和只读模型的函数一样,只是返回时需要增加Qt::ItemIsEditable,形式如下:

if(!index.isValid())
    return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditabled;

setData

bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)

data函数用来获取数据,setData函数用来设置模型索引index中存储的数据,形式如下:

if(index.isValid() && role == EditRole)
{
    int row = index.row();
    //根据行号从内部数据结构中定位,然后将其改编成value
    emit dataChanged(index, index);
    return true;
}
return false;

另外需要注意的是,在改变完内部数据后,需要发出dataChanged(index, index)信号通知Views和Delegate内部数据已经发生改变。

setHeaderData

bool QAbstractItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole)

headerData函数用于返回表头,这个函数设置表头为value,形式如下:

if(role != EditRole)
    return false;
if(orientation == Qt::Horizontal) 或 if(orientation == Qt::Vertical)
{
    //改变存储表头的变量
    emit headerDataChanged(orientation, section, section);
    return true;
}
return false;

同样,当改变表头后,需要发出headerDataChanged(orientation, section, section)信号,通知Views和Delegate。

如果需要添加和删除操作,则需要实现以下函数:
insertRows

bool QAbstractItemModel::insertRows(int position, int rows, const QModelIndex &index = QModelIndex())

这个函数在父级为index的结构中的第position行插入rows行。形式如下:

beginInsertRows(index, position, position + rows - 1);
//内部变量进行删除操作
endInsertRows();

return true;

进行插入操作时,前后分别需要调用beginInsertRows()和endInsertRows()函数。

removeRows

bool QAbstractItemModel::removeRows(int position, int rows, const QModelIndex &index = QModelIndex())

这个函数在父级为index的结构中的第position行删除rows行。形式如下:

beginRemoveRows(index, position, position + rows - 1);
//内部变量进行删除操作
endRemoveRows();
return true;

进行删除操作时,前后需要调用beginRemoveRows()和endRemoveRows()函数。

insertColumns
removeColumns
这两个函数同插入行和删除行类似,也需要调用两个函数。

通常情况下,函数应该返回true如果操作成功,但是如果只是部分操作成功,例如只插入了部分行,则需要返回false。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值