本文紧接着上一文介绍如何对用户模型剩下的类进行实现,并将其集成到视图并显示出来。
QAbstractItemModel
- insertRows(int row, int count, const QModelIndex &parent)
该函数实现一次性向表格之中从row位置开始插入count行,一个简单的实现手段是:
{
if(row<0 || row>m_dataVector->size() || count<1) return false;
beginInsertRows(parent, row, row + count - 1);
for (quint8 i=0;i<count;i++)
{
QPair<QString,int> newPair{};
m_dataVector->insert(row+i,newPair);
}
endInsertRows();
return true;
}
- insertColumns(int column, int count, const QModelIndex &parent)
由于表格固定是两列,所以此处不做处理。
{
beginInsertColumns(parent, column, column + count - 1);
endInsertColumns();
return false;
}
- removeRows(int row, int count, const QModelIndex &parent)
删除行与前面的方法类似,此处一个简单实现如下:
{
if(row<0 || row>m_dataVector->size() || count<1 ) return false;
beginRemoveRows(parent, row, row + count - 1);
for (quint8 i=0;i<count;i++)
{
if(m_dataVector->size()==0) return true; //判断是否已经删除完,如果已完,提前退出
m_dataVector->remove(row);
}
endRemoveRows();
return true;
}
- removeColumns(int column, int count, const QModelIndex &parent)
类似的,此处也不需要对删除列进行操作:
{
beginRemoveColumns(parent, column, column + count - 1);
// FIXME: Implement me!
endRemoveColumns();
return false;
}
下一步构造一个简单的视图,实现对数据的显示。进入main.cpp,对前期的界面显示代码进行注释:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// mainWindow w;
// w.show();
包含需要的头文件:
#include <QTableView>
#include "customtableview.h"
此处使用标准的QTableView来显示图标,构建一个视图,构建一个用户模型示例并将其设置为视图的模型,最后显示视图。
QTableView view; //构造视图
CustomItemModel model; //构造模型
view.setModel(&model); //设置视图的模型
view.show(); //显示视图
编译运行代码,显示表格:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200922161941895.png#pic_center)
再在用户模型之中加入一个写入数据的槽函数:
void CustomItemModel::pushData(QString &name, int &age)
{
m_dataVector->push_back(QPair<QString,int>(name,age));
m_checked.push_back(QPair<bool,bool>(false,false));
emit dataChanged(
createIndex(m_dataVector->size()-1,0),
createIndex(m_dataVector->size()-1,1),
QVector<int>() << Qt::DisplayRole<<Qt::CheckStateRole
);
}
调用对象的槽函数,写入几个测试数据:
model.pushData("Jim",28);
model.pushData("White",72);
运行模型:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200922162013781.png#pic_center)
后面我对数据其他一些角色进行修改,增加一个图标显示,比如Name列显示红色,Age列显示绿色。
case Qt::DecorationRole://The data to be rendered as a decoration in the form of an icon. (QColor, QIcon or QPixmap)
{
if(index.column()==0) return QColor(255,0,0,255); //Name显示 红色
if(index.column()==1) return QColor(0,255,0,255); //Age列显示绿色
}
运行代码:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200922162034715.png#pic_center)
再给每个条目显示一个勾选框:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200922162043734.png#pic_center)
为了方便交互,需要一个数据结构来记录勾选状态:
QVector<QPair<bool,bool>> m_checked{}; //储存某个位置是否被勾选
根据数据设置条目勾选状态:
case Qt::CheckStateRole:
{
if(index.column()==0)
{
if(m_checked.at(index.row()).first) return Qt::Checked;
else return Qt::Unchecked;
}
else {
if(m_checked.at(index.row()).second) return Qt::Checked;
else return Qt::Unchecked;
}
}
根据用户选择记录勾选状态:
case Qt::CheckStateRole:
{
auto cheked = m_checked.at(index.row());
if(index.column()==0)
{
m_checked.replace(index.row(),QPair<bool,bool>(value.toBool(),cheked.second));
}
else
{
m_checked.replace(index.row(),QPair<bool,bool>(cheked.first,value.toBool()));
}
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
为了测试,假设勾选框勾上,设置字体为粗体,如果没有勾选设置为非粗体。对data之中的FontRole编写算法如下:
case Qt::FontRole:
{
QFont font;
if(index.column()==0? m_checked.at(index.row()).first:m_checked.at(index.row()).second)
{
font.setBold(true);
return font;
}
else {
return font;
}
}
编译运行代码,勾选几个条目测试代码效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200922162144681.png#pic_center)
那么需要对insertRows和removeRows做相应的修改。修改后用户模型的头文件代码如下:
#ifndef CUSTOMITEMMODEL_H
#define CUSTOMITEMMODEL_H
#include <QAbstractItemModel>
#include <QVector>
#include <QMap>
class CustomItemModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit CustomItemModel(QObject *parent = nullptr);
~CustomItemModel();
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override;
// Basic functionality:
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
// Fetch data dynamically:
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// Editable:
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// Add data:
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;
// Remove data:
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;
public slots:
void pushData(QString,int ); //向模型添加一个数据
private:
QVector<QPair<QString,int>> *m_dataVector = nullptr; //新建一个储存数据的序列对象,每个元素(行)两个值,一个储存姓名、一个储存年龄
QList<QString> m_headName; //储存表头名称
QVector<QPair<bool,bool>> m_checked{}; //储存某个位置是否被勾选
};
#endif // CUSTOMITEMMODEL_H
用户模型源文件代码如下:
#include "customitemmodel.h"
#include <QDebug>
#include <QColor>
#include <QFont>
#include <iostream>
CustomItemModel::CustomItemModel(QObject *parent)
: QAbstractItemModel(parent)
,m_dataVector(new QVector<QPair<QString,int>>{})
{
m_dataVector->push_back({"Bob",30});
m_headName = {"Name","Age"};
m_checked.append(QPair<bool,bool>(false,false));
}
CustomItemModel::~CustomItemModel()
{
if(m_dataVector) {m_dataVector->clear();delete m_dataVector;m_dataVector=nullptr;}
}
QVariant CustomItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role!=Qt::DisplayRole)
return QVariant();
if(orientation==Qt::Vertical)
return QVariant(section);
if(orientation==Qt::Horizontal)
{
if(section==0) return QVariant(m_headName[0]);
if(section==1) return QVariant(m_headName[1]);
}
return QVariant();
}
bool CustomItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
if (value != headerData(section, orientation, role)) {
if(role!=Qt::EditRole) return false;
if(orientation==Qt::Vertical) return false;
if(section==0) m_headName[0]= value.toString();
if(section==1) m_headName[1]=value.toString();
emit headerDataChanged(orientation, section, section);
return true;
}
return false;
}
QModelIndex CustomItemModel::index(int row, int column, const QModelIndex &parent) const
{
if(row>m_dataVector->size()-1 || row < 0) return QModelIndex();
if(column>1 || column<0) return QModelIndex();
return createIndex(row,column);
}
QModelIndex CustomItemModel::parent(const QModelIndex &index) const
{
// FIXME: Implement me!
return QModelIndex();
}
int CustomItemModel::rowCount(const QModelIndex &parent) const
{
return m_dataVector->size();
}
int CustomItemModel::columnCount(const QModelIndex &parent) const
{
return 2;
}
bool CustomItemModel::hasChildren(const QModelIndex &parent) const
{
return false;
}
bool CustomItemModel::canFetchMore(const QModelIndex &parent) const
{
return false;
}
void CustomItemModel::fetchMore(const QModelIndex &parent)
{
// FIXME: Implement me!
}
QVariant CustomItemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if(index.row()>m_dataVector->size()-1 || index.row()<0 || index.column()<0 ||index.column()>1)
return QVariant();
switch(role)
{
case Qt::DisplayRole:
{
if(index.column()==0)
{
QString name = m_dataVector->at(index.row()).first;
return QVariant(name);
}
if(index.column()==1)
{
int age = m_dataVector->at(index.row()).second;
return QVariant(age);
}
break;
}
case Qt::TextAlignmentRole:
{
return Qt::AlignCenter;
}
case Qt::DecorationRole://The data to be rendered as a decoration in the form of an icon. (QColor, QIcon or QPixmap)
{
if(index.column()==0) return QColor(255,0,0,255); //Name显示 红色
if(index.column()==1) return QColor(0,255,0,255); //Age列显示绿色
}
case Qt::ToolTipRole:
{
return QVariant();
}
case Qt::SizeHintRole:
{
return QVariant();
}
case Qt::FontRole:
{
QFont font;
if(index.column()==0? m_checked.at(index.row()).first:m_checked.at(index.row()).second)
{
font.setBold(true);
return font;
}
else {
return font;
}
}
case Qt::BackgroundRole:
{
return QVariant();
}
case Qt::ForegroundRole:
{
return QVariant();
}
case Qt::CheckStateRole:
{
if(index.column()==0)
{
if(m_checked.at(index.row()).first) return Qt::Checked;
else return Qt::Unchecked;
}
else {
if(m_checked.at(index.row()).second) return Qt::Checked;
else return Qt::Unchecked;
}
}
case Qt::UserRole+1: //用户自定义角色 预留
{
return QVariant();
}
default:
return QVariant();
}
}
bool CustomItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (data(index, role) != value)
{
switch (role)
{
case Qt::EditRole:
{
QString name = m_dataVector->at(index.row()).first;
int age = m_dataVector->at(index.row()).second;
if(index.column()==0)
{
QPair<QString,int> pair(value.toString(),age);
m_dataVector->replace(index.row(),pair);
}
if(index.column()==1)
{
QPair<QString,int> pair(name,value.toInt());
m_dataVector->replace(index.row(),pair);
}
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
case Qt::CheckStateRole:
{
auto cheked = m_checked.at(index.row());
if(index.column()==0)
{
m_checked.replace(index.row(),QPair<bool,bool>(value.toBool(),cheked.second));
}
else
{
m_checked.replace(index.row(),QPair<bool,bool>(cheked.first,value.toBool()));
}
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
default:
return false;
}
}
return false;
}
Qt::ItemFlags CustomItemModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable |Qt::ItemIsUserCheckable; // FIXME: Implement me!
}
bool CustomItemModel::insertRows(int row, int count, const QModelIndex &parent)
{
if(row<0 || row>m_dataVector->size() || count<1) return false;
beginInsertRows(parent, row, row + count - 1);
for (quint8 i=0;i<count;i++)
{
QPair<QString,int> newPair{};
QPair<bool,bool> state{false,false};
m_dataVector->insert(row+i,newPair);
m_checked.insert(row+i,state);
}
endInsertRows();
return true;
}
bool CustomItemModel::insertColumns(int column, int count, const QModelIndex &parent)
{
beginInsertColumns(parent, column, column + count - 1);
endInsertColumns();
return false;
}
bool CustomItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
if(row<0 || row>m_dataVector->size() || count<1 ) return false;
beginRemoveRows(parent, row, row + count - 1);
for (quint8 i=0;i<count;i++)
{
if(m_dataVector->size()==0) return true; //判断是否已经删除完,如果已完,提前退出
m_dataVector->remove(row);
m_checked.remove(row);
}
endRemoveRows();
return true;
}
bool CustomItemModel::removeColumns(int column, int count, const QModelIndex &parent)
{
beginRemoveColumns(parent, column, column + count - 1);
// FIXME: Implement me!
endRemoveColumns();
return false;
}
void CustomItemModel::pushData(QString name, int age)
{
m_dataVector->push_back(QPair<QString,int>(name,age));
m_checked.push_back(QPair<bool,bool>(false,false));
emit dataChanged(
createIndex(m_dataVector->size()-1,0),
createIndex(m_dataVector->size()-1,1),
QVector<int>() << Qt::DisplayRole<<Qt::CheckStateRole
);
}
欢迎同好沟通交流,批评指正,欢迎关注我的公号:不如起而行之