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。