注意:以下可能有写错的地方,请见谅
本文尽量写的详细,为了以后的翻阅
提到模型就要说视图和数据,它们三者的关系是相辅相成的。
数据负责提供,模型负责中转存放,视图负责显示,这是它们的关系。
模型中有它自己的索引(QModelIndex),由很多索引构成了一个模型。
如果需要获取数据必须要通过索引来获取设置。
简单的不多说。下面是自定义模型时间
本文对QAbstractTableModel进行说明
知己知彼方能百战百胜。假设我们已经有了数据。
但是怎么放进模型里面呢。下面我们来说一下模型的简单构造
#ifndef MYMODELITEMMODEL_H
#define MYMODELITEMMODEL_H
#include
class MyModelItemModel : public QAbstractTableModel
{
public:
MyModelItemModel(QObject *parent);
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
bool getData(QVariant &OtherData);
private:
QList> m_data;
};
#endif // MYMODELITEMMODEL_H
首先我们要说明一下,自定义模型需要自己创建一个数据的载体,我们选择了QList
一个二维数组来盛放表格的数据。这个数据的载体大家可以按照自己的需要来分别设置不同的载体,例如QList *m_data都可以的。
我们既然有了数据,肯定要首先把数据塞到模型里面这样才对嘛。
那我们就先实现数据加载进入模型。
既然这样我们就需要首先实现加载数据的函数bool getData(QVariant &OtherData)
bool MyModelItemModel::getData(QVariant &OtherData)
{
// 因为咱们的盛放数据的格式为QList>;
// 所以要把这个OtherData的数据逐层分解
QVariantList varRows = OtherData.toList(); // 获得二维数组
if (varRows.isEmpty())
return false;
for (int i=0; i// 获得二维数组下某个一维数组
m_data.push_back(buf); // 把其他的数据压入自己构造的数据源
}
}
好,当我们的模型已经注入了新鲜的数据,剩下的事情肯定就是要把数据显示到视图里面来
视图里面通常用肉眼可以观察到一些东西:
现在我们模型里面已经有了数据,就可以实现模型返回给视图的数据了
QVariant MyModelItemModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int column = index.column();
if (!index.isValid()) // 如果index是无效的则返回无效的变体型
return QVariant();
if (role == Qt::DisplayRole){ // 判断角色,此角色有很多种,可以自己查看帮助来选择需要的
if (row >= m_data.size()) // 如果index的row大于数据的row
return QVariant();
if (column >= m_data.at(row).size()) // 如果index.column大于数据的最大column
return QVariant();
return m_data.at(row).at(column); // 都ok了则返回数据
}else{
return QVariant(); // 如果角色不是我们想要的则过滤掉
}
}
这样就实现了返回的数据,并且我们自己也剩下了我们想要的数据,不想要的都已经被过滤掉。
虽然有数据了,但是视图还是不知道要新建多少行号和列号正好来满足模型所需,不产生浪费
所以我们就要实现rowCount(const QModelINdex)
和column(const QModelIndex)
int MyModelItemModel::rowCount(const QModelIndex &parent) const
{
return m_data.size();
}
int MyModelItemModel::columnCount(const QModelIndex &parent) const
{
if (m_data.isEmpty())
return -1;
else
return m_data.at(0).size();
}
行和列的实现很简单,只需要返回模型的各种尺寸即可。
这样基本一个简单的模型就搞定了。大家可以试试啦。
我自己写了一个小例子,大家可以参照下
#include "widget.h"
#include
#include "mymodelitemmodel.h"
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QVariantList list;
QVariantList buf;
for (int i=1; i<=30; i++){
for (int j=1; j<=30; j++){
buf.append("app");
}
list.push_back(buf);
}
QVariant data(list);
MyModelItemModel *model = new MyModelItemModel();
model->getData(data);
QTableView *view = new QTableView();
view->setModel(model);
QGridLayout *layout = new QGridLayout();
layout->addWidget(view);
Widget w;
w.setLayout(layout);
w.show();
return a.exec();
}
有时候我们还想编辑模型,那么我们可以再自定义模型里面h文件声明
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
其中Qt::ItemFlags flags(const QModelIndex &index) const;
是表示索引的属性,基类实现了Qt::ItemIsSelectable
和Qt::ItemIsEnabled
两个默认的属性,如果我们需要编辑模型,首先要设置索引的属性
Qt::ItemFlags MyModelItemModel::flags(const QModelIndex &index) const
{
if (! index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; // 索引默认的属性需要添加的编辑属性
}
这样我们就设置好索引的属性了。
现在设置好属性了,双击表格内的索引也可以被修改了,但是你会发现修改的内容没有被正确显示。这是因为数据源里面的对应索引的数据没有被改变。我们因此还需要实现bool setData(const QModelIndex &index, const QVariant &value, int role)
bool MyModelItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (! index.isValid())
return false;
// 如果被修改的角色为editRolr,这个角色代表编辑角色
if (role == Qt::EditRole){
m_data[index.row()].replace(index.column(), value); // 我们需要先修改数据源里面的数据
emit dataChanged(index, index);
return true;
}
return false;
}
这个emit dataChanged(index, index);
是必须要发射的,如果你不发射这个数据修改的信号,视图是不会有所反应的,因为它不知道数据被修改了,咱们刚开始也说了,革命分工不同。所以我们发射这个信号,让视图来取数据。此时我们可以看看QVariant data(const QModelIndex &index, int role) const
这个返回数据的函数
QVariant MyModelItemModel::data(const QModelIndex &index, int role) const
{
........
........ // 上面的代码省略了
if (role == Qt::DisplayRole ){ // 判断角色,此角色有很多种,可以自己查看帮助来选择需要的
.......
......
}
此时if (role == Qt::DisplayRole)
这个角色是以文本来显示数据的,但是我们编辑索引之后,索引的属性发生了变化,增加了一个Qt::ItemIsEditable
,它个人的角色也产生了变化。所以我们需要添加上一个角色Qt::EditROle
if (role == Qt::DisplayRole || role == Qt::EditRole)
这样就成功了!
总体来讲,刚接触模型,会发现细节有很多,我们稍一疏忽就会出错。所以我们要记住模型是以索引为单位干活,角色为单位显示,这样想的话会比较容易理解一点。
博文如若写的有错误,请不吝指出,共同学习!