13.2.3 派生新模型类(7)
编辑完成后,Model/View框架会调用TreeModel的setData()函数。类型为QModelIndex的函数参数表示目标数据项的索引,类型为QVariant的函数参数存放编辑结果,函数参数role表示要修改目标数据项哪个角色对应的数据子项。程序员在QAbstractItemModel的派生类中重载这个setData()函数时,应该返回一个布尔类型的值。返回true,表示该函数成功地修改了目标数据项的值,返回false则表示未能成功修改。本例总是返回true。
行②的while循环语句沿着被编辑叶节点到根节点的路径,逐个修改满二叉树节点中的整数值。每修改一个,在行③触发一个dataChanged信号,通知视图对象重新绘制被修改的节点。
7.从QAbstractListModel及QAbstractTableModel派生新模型类
如果数据集是一个列表,我们没有必要直接从QAbstractItemModel派生新模型类,而是应该从该类的子类QAbstractListModel派生新模型类,因为后者已经替我们实现了部分功能,使得我们只需要做少量的工作,就可以实现一个新模型类。QAbstractListModel是一个抽象类,使用该类的唯一方式是派生新模型类,程序员并不能直接使用该类。
具体地说,新模型类只需重载QAbstractListModel的rowCount()以及data()函数,前者返回列表中元素的个数,后者返回与某个数据项中指定角色对应的数据子项。如果应用程序需要修改列表中的元素,新模型类还应该重载flags()以及setData()函数,前者返回一个标志变量,表示模型中的数据集是可编辑的,委托对象就会编辑列表中的数据项。编辑完毕后,就会调用setData()函数,将数据写回模型。
例如,设新模型类ListModel内部定义了一个数组int numbers[N],其中常量N表示数组元素的个数。为了显示、编辑这个列表,该模型类应该如代码段13 9所示那样重载QAbstractListModel的虚函数。由于列表中数据项的个数是固定的,函数rowCount()只是简单地返回这个数值。函数data()中,类型为QModelIndex的函数参数是列表中某个数据项的索引。由于数据集是一个列表,这个索引内部存放的指针(或内部ID)、列号没有任何作用,该索引内部存放的行号就可以唯一地确定列表中的某个数据项。函数flags()返回的标志变量包含Qt::ItemIsEditable,表明用户可以编辑列表中的数据项。用户完成编辑后,新模型类的setData()函数会被调用,类型为QModelIndex的函数参数是被编辑数据项的索引,该索引内部存放的行号表示目标数据项在列表中的序号。
代码段13 9,重载QAbstractListModel的虚函数以显示、编辑一个列表,取自z:\examples\mvc\QAbstractListModel_demo\ListModel.cpp
- ListModel::ListModel(QObject *parent) :QAbstractListModel(parent)
- {
- for (int i=0;i<N;i++)
- numbers[i] = i;
- }
- int ListModel::rowCount(const QModelIndex & /*parent*/) const
- {
- return N;
- }
- QVariant ListModel::data(const QModelIndex &index, int role) const
- {
- if (role == Qt::DisplayRole) {
- int idx = index.row();
- return QVariant( numbers[idx] );
- }
- return QVariant();
- }
- Qt::ItemFlags ListModel::flags(const QModelIndex & index) const
- {
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
- }
- bool ListModel::setData(const QModelIndex & index, const QVariant & value, int role)
- {
- if (role != Qt::EditRole) return true;
- int idx = index.row();
- numbers[idx] = value.toInt();
- return true;
- }
如果数据集是一个表格,我们也没有必要直接从QAbstractItemModel派生新模型类,而是应该从该类的子类QAbstractTableModel派生新模型类。具体步骤与从QAbstractListModel派生新模型类的步骤类似,读者可以参考随书光盘z:\examples\mvc\QAbstractTableModel_ demo下的例子。