前一篇水文中,老周演示了 QAbstractItemModel 抽象类的继承方法。其实,在 Qt 的库里面,QAbstractItemModel 类也派生了两个基类,能让开发者继承起来【稍稍】轻松一些。
这两个类是 QAbstractListModel 和 QAbstractTableModel。
- QAbstractListModel类专门用来实现一维列表结构模型的。它实现了 index、parent 等方法,并且把 columnCount 方法变成了私有成员(一维列表不需要它)。继承时直接实现 rowCount、data、setData 这几个方法即可;
- QAbstractTableModel类专门用来实现二维表结构的模型。它实现了 index、parent 等方法。继承时咱们要实现 rowCount、columnCount、data、setData 等方法。
虽然它帮咱们实现了一些成员,但实际上也省不了多功夫的。下面咱们用 QAbstractListModel 举例,和上篇中的一样,操作一个 QList<int> 数据。毕竟一维的比较简单,演示出来大伙都容易懂。
// 头文件 #ifndef LIST_H #define LIST_H #include <QAbstractListModel> #include <QList> class CustListModel: public QAbstractListModel { Q_OBJECT public: explicit CustListModel(QObject* parent = nullptr); ~CustListModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; Qt::ItemFlags flags(const QModelIndex &index) const override; private: // 私有,存数据用 QList<int> m_list; }; #endif
先弄 rowCount 方法,返回总行数,这个简单一点。
int CustListModel::rowCount(const QModelIndex &parent) const { if(parent.isValid()) { return 0; } return m_list.size(); }
实现 data 方法,获取数据时用。
QVariant CustListModel::data(const QModelIndex &index, int role) const { if( role == Qt::DisplayRole || role == Qt::EditRole) { if(!index.isValid()) { return QVariant(); } // 获取索引 int i = index.row(); // 返回指定索引处的值 return m_list.at(i); } return QVariant(); }
要返回 QList<T> 中指定的元素,用 at 方法,传递索引给它即可。
实现 setData 方法,编辑结束后用于更新数据的。
bool CustListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; if(role == Qt::EditRole || role == Qt::DisplayRole) { // 解包数据 bool ok; int val = value.toInt(&ok); if(ok) { // 设置值 m_list.replace(index.row(), val);
// 发出信号
emit dataChanged(index,index,{role});
return true; } } return false; }
要修改某个索引处的值,用 replace 方法替换。
实现 flags 方法,表明该模型的列表项支持交互、编辑、被选择。
Qt::ItemFlags CustListModel::flags(const QModelIndex &index) const { return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable; }
ItemIsEnabled 表明列表项是活动的,用户可操作的;ItemIsEditable 表明列表项可编辑;ItemIsSelectable 表示列表项可以选择。
下面这两个方法是有点麻烦的,这里先介绍一下。
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
row 参数表示要插入元素的索引,count 表示插入元素个数,插入元素后,原来的元素向后移动。例如:A、B、C、D,现在我要在B处插入两个元素。那么 row = 1,count = 2,B 处放入 E、F,B、C、D 向后移,变成 A、E、F、B、C、D。
然后,删除元素的方法也类似。
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
row 是要删除的索引,count 是连续要删掉的元素个数。
insertRows 和 removeRows 方法的返回值含义相同:成功返回 true,失败返回 false。
好,现在上代码。
bool CustListModel::insertRows(int row, int count, const QModelIndex &parent) { if(parent.isValid()) return false; // 注意这里!! beginInsertRows(parent, row, row + count - 1); // 开始插入 m_list