一、需要在模型子类中实现的函数可以分为三组:
1、项数据的处理:所有模型都需要实现一些功能,以使视图和委托能够查询模型的维度、检查项和检索数据。
2、导航和索引创建:层次模型需要提供视图可以调用的函数来导航它们公开的树状结构,并获得项目的模型索引。
3、拖放支持和MIME类型处理:模型继承了控制内部和外部拖放操作执行方式的函数。这些函数允许用其他组件和应用程序能够理解的MIME类型来描述数据项。
二、项数据的处理
1、只读模型:
flags() | 其他组件用于获取关于模型提供的每个项的信息。在许多模型中,标志的组合应该包括Qt::ItemIsEnabled和Qt::ItemIsSelectable。 |
data() | 用于向视图和委托提供项数据。通常,模型只需要为Qt::DisplayRole和任何特定于应用程序的用户角色提供数据,但也可以为Qt::ToolTipRole、Qt::AccessibleTextRole和Qt::AccessibleDescriptionRole提供数据 |
headerData() | 提供视图的标题中显示的信息。信息仅由能够显示头信息的视图检索。 |
rowCount() | 模型数据的行数 |
columnCount() | 提供模型公开的数据列的数量。List模型不提供此功能,因为它已经在QAbstractListModel中实现。 |
#include
class StringListModel : public QAbstractListModel
{
public:
StringListModel(const QStringList stringList, QObject *parent = 0);
protected:
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
private:
QStringList m_stringList;
};
StringListModel::StringListModel(const QStringList stringList, QObject *parent)
:QAbstractListModel(parent),m_stringList(stringList)
{
}
Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index);//默认是ItemIsEnabled ItemIsSelectable
}
QVariant StringListModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(index.row() > m_stringList.size())
return QVariant();
if(role == Qt::DisplayRole)
return m_stringList.at(index.row());
else
return QVariant();
}
QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();
if(orientation == Qt::Horizontal)
return QString("Column %1").arg(section);//列号
if(orientation == Qt::Vertical)
return QString("Row %1").arg(section);//行号
}
int StringListModel::rowCount(const QModelIndex &parent) const
{
return m_stringList.count();
}
#include
class TableModel : public QAbstractTableModel
{
public:
TableModel(const int row, const int column, QObject *parent = 0);
protected:
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
private:
int m_row;
int m_column;
};
TableModel::TableModel(const int row, const int column, QObject *parent)
:QAbstractTableModel(parent),m_row(row),m_column(column)
{
}
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{
QAbstractTableModel::flags(index);
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role == Qt::DisplayRole)
return QString("(%1,%2)").arg(index.column()).arg(index.row());
else
return QVariant();
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return m_row;
}
int TableModel::columnCount(const QModelIndex &parent) const
{
return m_column;
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();
if(orientation == Qt::Horizontal)
return QString("Column %1").arg(section);
if(orientation == Qt::Vertical)
return QString("Row %1").arg(section);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QGroupBox *listGroup = new QGroupBox("只读列表");
QVBoxLayout *listLayout = new QVBoxLayout;
QListView *listView = new QListView;
StringListModel *stringListModel = new StringListModel(QStringList()<<"111"<<"222"<<"333");
listView->setModel(stringListModel);
listLayout->addWidget(listView);
listGroup->setLayout(listLayout);
QGroupBox *tableGroup = new QGroupBox("只读表格");
QHBoxLayout *tableLayout = new QHBoxLayout;
QTableView *tableView = new QTableView;
tableView->verticalHeader()->hide();//不显示垂直标题栏
TableModel *tableModel = new TableModel(3,3);
tableView->setModel(tableModel);
tableLayout->addWidget(tableView);
tableGroup->setLayout(tableLayout);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(listGroup);
mainLayout->addWidget(tableGroup);
w.setLayout(mainLayout);
w.show();
return a.exec();
}
2、编辑模型:
flags() | 除了返回只读的枚举值 还要加上 Qt::ItemIsEditable |
setData() | 用于修改与指定模型索引关联的数据项。为了能够接受用户界面元素提供的用户输入,这个函数必须处理与Qt::EditRole关联的数据。更改数据项之后,模型必须发出dataChanged()信号,以通知更改的其他组件。 |
setHeaderData() | 用于修改水平和垂直标题信息。更改数据项之后,模型必须发出headerDataChanged()信号,以通知更改的其他组件。 |
#include
class TableModel : public QAbstractTableModel
{
public:
TableModel(const int row, const int column, QObject *parent = 0);
protected:
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
//为编辑实现
bool setData(const QModelIndex &index, const QVariant &value, int role);
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
private:
int m_row;
int m_column;
QVector> vectors;
QStringList headerStrings;
};
TableModel::TableModel(const int row, const int column, QObject *parent)
:QAbstractTableModel(parent),m_row(row),m_column(column)
{
for(int i = 0; i < row; ++i){
QVector vector;
for(int j = 0; j < column; ++j){
vector << QString("(%1,%2)").arg(i).arg(j);
headerStrings << QString("Column %1").arg(j);
}
vectors << vector;
}
}
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractTableModel::flags(index)|Qt::ItemIsEditable;
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role == Qt::DisplayRole || role == Qt::EditRole){
QVector vector = vectors.at(index.row());
return vector.at(index.column());
}
else
return QVariant();
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return m_row;
}
int TableModel::columnCount(const QModelIndex &parent) const
{
return m_column;
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();
if(orientation == Qt::Horizontal)
return headerStrings.at(section);
return QVariant();
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(index.isValid() && role == Qt::EditRole){
QVector vector = vectors.at(index.row());
vector.replace(index.column(),value.toString());
vectors.replace(index.row(),vector);
//发出信号 通知 data函数重新读取数据
dataChanged(index,index);
return true;
}
return false;
}
bool TableModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
if(role != Qt::EditRole && orientation == Qt::Horizontal){
headerStrings.replace(section,value.toString());
headerDataChanged(Qt::Horizontal,section,section);
return true;
}
return false;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QGroupBox *tableGroup = new QGroupBox("只读表格");
QHBoxLayout *tableLayout = new QHBoxLayout;
QTableView *tableView = new QTableView;
tableView->verticalHeader()->hide();//不显示垂直标题栏
TableModel *tableModel = new TableModel(3,3);
tableView->setModel(tableModel);
tableLayout->addWidget(tableView);
tableGroup->setLayout(tableLayout);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(tableGroup);
w.setLayout(mainLayout);
w.show();
return a.exec();
}
3、插入删除模型:
insertRows() | 用于向所有类型的模型中添加新的数据行和数据项。实现必须在将新行插入任何底层数据结构之前调用beginInsertRows(),然后立即调用endInsertRows()。 |
removeRows() | 用于从所有类型的模型中删除行及其包含的数据项。实现必须在从任何底层数据结构中删除行之前调用beginRemoveRows(),然后立即调用endRemoveRows()。 |
insertColumns() | 用于向表模型和层次模型添加新的列和数据项。实现必须在将新列插入任何底层数据结构之前调用beginInsertColumns(),然后立即调用endInsertColumns()。 |
removeColumns() | 用于从表模型和层次模型中删除列及其包含的数据项。实现必须在从任何底层数据结构中删除列之前调用beginRemoveColumns(),然后立即调用endRemoveColumns()。 |
#include
class TableModel : public QAbstractTableModel
{
public:
TableModel(const int row, const int column, QObject *parent = 0);
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
//为编辑实现
bool setData(const QModelIndex &index, const QVariant &value, int role);
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
//为插入和删除实现
bool insertRows(int row, int count, const QModelIndex &parent);
bool removeRows(int row, int count, const QModelIndex &parent);
bool insertColumns(int column, int count, const QModelIndex &parent);
bool removeColumns(int column, int count, const QModelIndex &parent);
private:
int m_row;
int m_column;
QVector> vectors;
QStringList headerStrings;
};
TableModel::TableModel(const int row, const int column, QObject *parent)
:QAbstractTableModel(parent),m_row(row),m_column(column)
{
for(int i = 0; i < row; ++i){
QVector vector;
for(int j = 0; j < column; ++j){
vector << QString("(%1,%2)").arg(i).arg(j);
}
vectors << vector;
}
for(int i = 0; i < column; ++i)
headerStrings << QString("Column %1").arg(i);
}
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractTableModel::flags(index)|Qt::ItemIsEditable;
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role == Qt::DisplayRole || role == Qt::EditRole){
QVector vector = vectors.at(index.row());
return vector.at(index.column());
}
else
return QVariant();
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return m_row;
}
int TableModel::columnCount(const QModelIndex &parent) const
{
return m_column;
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();
if(orientation == Qt::Horizontal)
return headerStrings.at(section);
return QVariant();
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(index.isValid() && role == Qt::EditRole){
QVector vector = vectors.at(index.row());
vector.replace(index.column(),value.toString());
vectors.replace(index.row(),vector);
//发出信号 通知 data函数重新读取数据
dataChanged(index,index);
return true;
}
return false;
}
bool TableModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
if(role != Qt::EditRole && orientation == Qt::Horizontal){
headerStrings.replace(section,value.toString());
headerDataChanged(Qt::Horizontal,section,section);
return true;
}
return false;
}
bool TableModel::insertRows(int row, int count, const QModelIndex &parent)
{
beginInsertRows(parent,row,row+count-1);
for(int i = 0; i < count; ++i){
QVector vector;
for(int j = 0; j < m_column; ++j){
vector << QString("新插入");
}
vectors.insert(row,vector);
m_row++;
}
endInsertRows();
return true;
}
bool TableModel::removeRows(int row, int count, const QModelIndex &parent)
{
if(vectors.isEmpty() || m_row < row + count)
return false;
beginRemoveRows(parent,row,row+count-1);
for(int i = 0; i < count; ++i){
vectors.remove(row);
m_row--;
}
endRemoveRows();
}
bool TableModel::insertColumns(int column, int count, const QModelIndex &parent)
{
beginInsertColumns(parent,column,column+count-1);
//遍历每一行 为其增加count个元素
for(int i = 0; i < m_row; ++i){
QVector vector = vectors.at(i);
for(int j = 0; j < count; ++j){
vector.insert(column,QString("新增的%1").arg(j));
}
vectors.replace(i,vector);
}
//标题头要更新
for(int i = 0; i < count; ++i){
headerStrings << QString("Column %1").arg(m_column++);
}
endInsertColumns();
}
bool TableModel::removeColumns(int column, int count, const QModelIndex &parent)
{
if(vectors.isEmpty() || m_column < column + count)
return false;
beginRemoveColumns(parent,column,column+count-1);
for(int i = 0; i < m_row; ++i){
QVector vector = vectors.at(i);
for(int j = 0; j < count; ++j){
vector.remove(column);
}
vectors.replace(i,vector);
}
//删除标题头
for(int i = 0; i < count; ++i){
headerStrings.removeAt(column);
m_column--;
}
endRemoveColumns();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow window;
QAction *removeRow = window.menuBar()->addAction("删除行");
QAction *insertRow = window.menuBar()->addAction("插入行");
QAction *removeColumn = window.menuBar()->addAction("删除列");
QAction *insertColumn = window.menuBar()->addAction("插入列");
QTableView *view = new QTableView;
// view->setSelectionBehavior(QTableView::SelectRows);//选中单个item时 表现为选中了行
view->setSelectionBehavior(QTableView::SelectColumns);//选中单个item时 表现为选中了列
TableModel *model = new TableModel(3,3);
view->setModel(model);
QObject::connect(removeRow,&QAction::triggered,[=]{
if(view->selectionModel()->hasSelection()){
QModelIndex currentIndex = view->selectionModel()->currentIndex();
model->removeRows(currentIndex.row(),1,QModelIndex());
}else{
model->removeRows(0,1,QModelIndex());
}
});
QObject::connect(insertRow,&QAction::triggered,[=]{
if(view->selectionModel()->hasSelection()){
QModelIndex currentIndex = view->selectionModel()->currentIndex();
model->insertRows(currentIndex.row(),1,QModelIndex());
}else{
model->insertRows(model->rowCount(QModelIndex()),1,QModelIndex());
}
});
QObject::connect(removeColumn,&QAction::triggered,[=]{
if(view->selectionModel()->hasSelection()){
QModelIndex index = view->selectionModel()->currentIndex();
model->removeColumns(index.column(),1,QModelIndex());
}else{
model->removeColumns(model->columnCount(QModelIndex()),1,QModelIndex());
}
});
QObject::connect(insertColumn,&QAction::triggered,[=]{
if(view->selectionModel()->hasSelection()){
QModelIndex index = view->selectionModel()->currentIndex();
model->insertColumns(index.column(),1,QModelIndex());
}else{
model->insertColumns(model->columnCount(QModelIndex()),1,QModelIndex());
}
});
window.setCentralWidget(view);
window.show();
return a.exec();
}
三、自定义模型索引
index() | 给定父项的模型索引,此函数允许视图和委托访问该项的子项。如果找不到与指定的行、列和父模型索引对应的有效子项,则函数必须返回QModelIndex(),这是一个无效的模型索引。 |
parent() | 提供与任何给定子项的父项对应的模型索引。如果指定的模型索引对应于模型中的顶级项,或者如果模型中没有有效的父项,那么该函数必须返回一个无效的模型索引,该索引是用空的QModelIndex()构造函数创建的。 |
上面的两个函数都使用createIndex()工厂函数来生成供其他组件使用的索引。模型通常为这个函数提供一些惟一的标识符,以确保模型索引稍后可以与其对应的项重新关联。
四、拖放支持和MIME类型处理