1,自定义模型
#ifndef MYMODEL_H
#define MYMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QDebug>
/***
* 自定义表格视图
*/
class MyModel : public QAbstractItemModel
{
public:
int col,rw; //表格模型的列数和行数
QList<QVariant> s1; //这是模型管理的数据
QList<int> rol; //存储数据的角色
//构造函数表示创建一个 i 行 j 列的表格模型
MyModel(int i, int j);
//①、返回表格模型的行数
int rowCount(const QModelIndex &parent = QModelIndex ()) const;
//②、返回表格模型的列数
int columnCount(const QModelIndex &parent =QModelIndex ()) const;
//③、返回表格模型的父索引,因为所有单元格都是顶级节点,所以使用无效节点作为父节点
QModelIndex parent(const QModelIndex &index) const;
//④、为每个单元格创一个唯一的索引
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex ()) const;
//⑤、返回视图上显示的数据,该函数会被视图多次调用(注:其他虚函数同样会被 Qt 调用多次)
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
//⑥、重载 setData 以使用户可以向模型中添加数据
bool setData(const QModelIndex &index,const QVariant &value,
int role=Qt :: EditRole);
};
#endif // MYMODEL_H
#include "mymodel.h"
MyModel::MyModel(int i, int j)
: rw(i),col(j)
{
for(int k=0;k<col*rw;k++)
{
s1<<QVariant();
rol << -1;
}
}
int MyModel::rowCount(const QModelIndex &parent) const
{
return rw;
}
int MyModel::columnCount(const QModelIndex &parent) const
{
return col;
}
QModelIndex MyModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}
QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
qDebug()<<"index"<<endl; //测试语句
return createIndex(row, column, (void*)&(s1.at(row*column+column)));
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
//测试用,可看到每次调用时 Qt 内部传递进来的 role 的值。
qDebug()<<"data="<<role<<endl;
//单元格所在数据 s1 中的位置
int i=index.row()*col+index.column();
//以下对不同角色返回的数据会被视图使用以使数据正确的显示在其单元格中。
/***
* 对于 QListView 和 QTreeView 必须对所在节点设置大小,
* 否则这两个视图不会显示任何内容(因为节点大小为 0)
* if(role==Qt::SizeHintRole)
* return QSize(55,55);
*/
/***
* 设置角色 CheckStateRole 的数据,本示例返回一个无效的 QVariant
* 作为该角色的数据,若返回有效值,会使单元格的左侧显示一个复选框。
*/
if(role==Qt::CheckStateRole)
return QVariant();
//设置单元格中数据的对齐方式,本示例为左侧垂直居中对齐
if(role==Qt::TextAlignmentRole)
return Qt::AlignLeft|Qt::AlignVCenter;
//设置角色 DecorationRole(图片)的数据
if(role==Qt::DecorationRole)
//若用户设置了 DecorationRole 角色的数据,则返回用户为该单元格设置的数据。
if(rol.at(i)==Qt::DecorationRole)
return s1.at(i);
// 若用户为角色 EditRole 或 DisplayRole 角色设置了数据,
// 则返回用户为该单元格设置的数据。
if(role==Qt::EditRole|role==Qt::DisplayRole)
if(rol.at(i)==Qt::EditRole|rol.at(i)==Qt::DisplayRole)
return s1.at(i);
//其余角色使用无效数据
return QVariant();
}
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// 使用数据 value 和角色 role 分别替换列表 s1 和 rol 中原有的值,
s1.replace(index.row()*col+index.column(),value);
rol.replace(index.row()*col+index.column(),role);
//数据改变后,需要发送此信号。
emit dataChanged(index, index);
//返回 true,表示数据设置成功。
return true;
}
#include <QApplication>
#include "mymodel.h"
#include <QTableView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//创建一个 3 行 3 列的表格模型
MyModel *d=new MyModel(3,3);
QTableView *pv2=new QTableView;
/*也可使用以下视图来显示模型 d 中的数据,但要去掉 data()中
* 对 if(role==Qt::SizeHintRole)的注释,
* * QListView *pv2=new QListView;
* * QTreeView *pv2=new QTreeView;
* 还要注意,若使用树形视图显示,有可能会使程序崩溃,
* 因为本示例未完整的实现树形模型结构
*/
//向模型中添加数据
d->setData(d->index(0,0,QModelIndex()),"111",Qt::DisplayRole);
d->setData(d->index(1,0,QModelIndex()),222);
d->setData(d->index(1,1,QModelIndex()),333);
d->setData(d->index(2,1,QModelIndex()),QIcon("C:/sinux/lark/logo.png"),
Qt::DecorationRole);
pv2->setModel(d);
pv2->resize(333,222);
pv2->show();
return a.exec();
}
2,拖放
main.cpp中加入
/***
* 和拖动相关的设置
*/
pv2->setDragEnabled(true);
pv2->viewport()->setAcceptDrops(true);
pv2->setDropIndicatorShown(true);
pv2->setDragDropMode(QAbstractItemView::DragDrop);
增加函数定义
/*********************以下可拖动相关*********************/
//①、重新实现 flags 函数,以开启模型的拖放功能
Qt::ItemFlags flags(const QModelIndex &index) const;
//②、重新实现 supportedDropActions 以使模型即可复制又可移动
Qt::DropActions supportedDropActions() const;
//③、重新实现 canDropMimeData 函数,以决定单元格是否允许放置拖动过来的数据。
bool canDropMimeData(const QMimeData *data,Qt::DropAction action,
int row, int column,
const QModelIndex &parent) const;
//④、重新实现 mimeData 函数,以编码拖动时的数据(就是把拖动时的数据保存起来,并返回)
QMimeData* mimeData(const QModelIndexList &indexes) const;
//⑤、重新实现 dropMimeData 函数,以解码拖动时的数据并对其进行处理
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent);
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEditable|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|
Qt::ItemIsEnabled|Qt::ItemIsSelectable;
}
Qt::DropActions MyModel::supportedDropActions() const
{
return Qt::CopyAction|Qt::MoveAction;
}
bool MyModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
/***
* 本示例 parent 参数有效,row 和 column 是无效的,
* 以下代码表示,第 3 行第 3 列不接受来自拖放的数据。
*/
if(parent.row()==2&&parent.column()==2)
return false;
else
return true;
}
QMimeData *MyModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *const pm=new QMimeData(); //创建一个 QMimeData 对象。
/***
* 注意:indexes 是被拖动的数据项的索引,
* 若同时拖动了多个数据项,
* 则 indexes 才会包含多个数据项。
*/
for(int k=0;k<indexes.size();k++)
{
const QModelIndex &i=indexes.at(k);
if (i.isValid())
{
QByteArray t;
//提取数据项的索引为 i 角色为 DisplayRole 的数据
QVariant v=data(i, Qt::DisplayRole);
//把提取的数据保存在 t 之中
t.append(v.toString());
//然后把 t 中的数据以 MIME 类型的形式编码到 pm 中。
pm->setData("text/plain", t);
}
}
return pm; //返回编码后的数据
}
bool MyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
/***
* 本示例 parent 参数有效,row 和 column 无效,
* 以下代码表示保存单元格所在数据 s1 中的位置。
* 索引 parent 是拖动之后需要放置的位置的索引。
*/
int i=parent.row()*col+parent.column();
//解码由 mimeData 函数编码后的数据。
QByteArray t=data->data("text/plain");
//若是复制操作,则把新位置 parent 处的数据重置为 t
if(action==Qt::CopyAction)
{
setData(parent,t);
}
//若是移动操作,则把新位置 parent 处的数据重置为 t
if(action==Qt::MoveAction)
{
setData(parent,t);
}
//返回 true 表示数据已处理
return true;
}