前言
因为工作需要,对Qt的Model/View/Degelate进行了学习,事实证明,实践是学习的最佳途径。
主要内容:
1.自定义实现Model功能,各虚函数的功能作用与实现。
2.自定义degelate功能,各虚函数的功能作用与实现。
3.实现复选框选择功能、绘制进度条、QComboBox、QSpinBox控制输入等。
界面
代码
MyTableModel.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include <QAbstractTableModel>
struct DataModel
{
bool check;
QString name;
qint32 age;
int sex;
qint32 skill;
};
struct HeaderParam
{
QString title;
Qt::Alignment align;
};
class MyTableDelegate;
class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
friend class MyTableDelegate;
public:
~MyTableModel();
MyTableModel(QObject* parent = Q_NULLPTR);
// 控制 TableView 水平标题显示
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
// 指示 TableView 行列数目
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
// QModelIndex索引控制
QModelIndex parent(const QModelIndex& index) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
// 控制 Model 数据读写
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
// 添加条目
void addData(const DataModel& item);
enum
{
HEADER_CHECK = 0,
HEADER_NAME,
HEADER_AGE,
HEADER_SEX,
HEADER_SKILL,
};
private:
void initialize();
private:
QList<DataModel> datas;
QList<HeaderParam> header;
};
#endif // MYTABLEVIEW_H
MyTableModel.cpp
#include "mytablemodel.h"
MyTableModel::~MyTableModel()
{
}
MyTableModel::MyTableModel(QObject* parent)
: QAbstractTableModel(parent)
{
initialize();
}
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || section < 0 || section >= header.size())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
// 控制标题文字显示
return header.at(section).title;
}
else if (role == Qt::TextAlignmentRole)
{
// 控制标题文字对齐显示
return (uint)header.at(section).align;
}
/*由此看出 role 在模型/视图结构中指定交换数据的类型描述*/
return QVariant();
}
/*
返回 parent 索引所包含的行数
其中,无效的 QModelIndex表示根索引。我的视图只有6行5列的table表格,没有树形结构嵌套,所以只在无效索引时返回数据行数,而有效的索引由于没有子项数据,所以返回0值。
*/
int MyTableModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return datas.size();
}
/*
返回 parent 索引所包含的列数
其中,无效的 QModelIndex表示根索引。我的视图只有6行5列的table表格,没有树形结构嵌套,所以只在无效索引时返回数据列数,而有效的索引由于没有子项数据,所以返回0值。
*/
int MyTableModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return header.size();
}
/*
返回index索引的父级索引
我的视图只有6行5列的table表格,没有树形结构嵌套,所以任何索引的父级索引都返回无效索引。
*/
QModelIndex MyTableModel::parent(const QModelIndex& index) const
{
return QModelIndex();
}
/*
返回parent索引的第row行第column列索引
我的视图只有6行5列的table表格,没有树形结构嵌套,所以任何有效索引的子项都是空索引。而根索引的子项则是table表中的每一项数据。
*/
QModelIndex MyTableModel::index(int row, int column, const QModelIndex& parent) const
{
if (parent.isValid() || row < 0 || row >= datas.size() || column < 0 || column >= header.size())
{
return QModelIndex();
}
return createIndex(row, column);
}
// 获取 index 的 role 指定类型的数据
QVariant MyTableModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
int row = index.row();
int column = index.column();
if (row < 0 || row >= datas.size() || column < 0 || column >= header.size())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch (column)
{
case HEADER_CHECK:
return datas.at(row).check;
break;
case HEADER_NAME:
return datas.at(row).name;
break;
case HEADER_AGE:
return datas.at(row).age;
break;
case HEADER_SEX:
return datas.at(row).sex;
break;
case HEADER_SKILL:
return datas.at(row).skill;
break;
default:
break;
}
}
else if (role == Qt::TextAlignmentRole)
{
return (uint)header.at(column).align;
}
return QVariant();
}
// 获取 index 的标识,可用来控制项的读写权限等
Qt::ItemFlags MyTableModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
{
return Qt::NoItemFlags;
}
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
int column = index.column();
if (column >= 0 && column < header.size() && column > HEADER_CHECK && column < HEADER_SKILL)
{
flags |= Qt::ItemIsEditable;
}
return flags;
}
// 设置 index 的 role 类型数据
bool MyTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
{
return false;
}
int row = index.row();
int column = index.column();
switch (column)
{
case HEADER_CHECK:
datas[row].check = value.toBool();
break;
case HEADER_NAME:
datas[row].name = value.toString();
break;
case HEADER_AGE:
datas[row].age = value.toInt();
break;
case HEADER_SEX:
datas[row].sex = value.toInt();
break;
case HEADER_SKILL:
datas[row].skill = value.toInt();
break;
default:
break;
}
// 数据设置好通知视图更新显示
emit dataChanged(index, index, {Qt::DisplayRole});
return true;
}
void MyTableModel::initialize()
{
header.append({"", Qt::AlignCenter});
header.append({"姓名", Qt::AlignLeft | Qt::AlignVCenter});
header.append({"年龄", Qt::AlignLeft | Qt::AlignVCenter});
header.append({"性别", Qt::AlignLeft | Qt::AlignVCenter});
header.append({"技能点", Qt::AlignCenter});
}
void MyTableModel::addData(const DataModel& item)
{
beginInsertRows(QModelIndex(), datas.size(), datas.size());
datas.append(item);
endInsertRows();
}
MyTableDelegate.h
#ifndef MYTABLEITEMDELEGATE_H
#define MYTABLEITEMDELEGATE_H
#include <QStyledItemDelegate>
class MyTableDelegate : public QStyledItemDelegate
{
public:
~MyTableDelegate();
MyTableDelegate(QObject* parent = Q_NULLPTR);
// 编辑控件控制
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const;
// 数据交换
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
// 控制单元格显示
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
// 实现特定编辑业务
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index);
};
#endif // MYTABLEITEMDELEGATE_H
MyTableDelegate.cpp
#include <QPainter>
#include <QLineEdit>
#include <QSpinBox>
#include <QComboBox>
#include <QCheckBox>
#include <QMouseEvent>
#include <QApplication>
#include <QAbstractItemView>
#include "mytabledelegate.h"
#include "mytablemodel.h"
MyTableDelegate::~MyTableDelegate()
{
}
MyTableDelegate::MyTableDelegate(QObject* parent)
: QStyledItemDelegate(parent)
{
}
void MyTableDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionViewItem opt(option);
opt.displayAlignment = (Qt::Alignment)index.data(Qt::TextAlignmentRole).toUInt();
if (opt.state & QStyle::State_HasFocus)
{
opt.state ^= QStyle::State_HasFocus;
}
if (option.state & QStyle::State_Selected)
{
opt.rect.setBottom(option.rect.bottom() - 1);
opt.backgroundBrush = option.state & QStyle::State_Active
? option.palette.brush(QPalette::Active, QPalette::Highlight)
: option.palette.brush(QPalette::Inactive, QPalette::Highlight);
}
if (index.column() == MyTableModel::HEADER_SEX)
{
// 控制性别列显示
opt.text = index.data().toInt() > 0 ? "男" : "女";
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
}
else if (index.column() == MyTableModel::HEADER_CHECK)
{
// 控制 check 列显示,QCheckBox 标识
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
QStyleOptionButton checkBoxStyle;
checkBoxStyle.state = index.data().toBool() ? QStyle::State_On : QStyle::State_Off;
checkBoxStyle.state |= QStyle::State_Enabled;
checkBoxStyle.rect = option.rect;
QCheckBox checkBox;
QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkBoxStyle, painter, &checkBox);
}
else if (index.column() == MyTableModel::HEADER_SKILL)
{
// 控制技能点列显示,QProgressBar 显示
int num = index.data().toInt();
QStyleOptionProgressBar barOpt;
barOpt.rect = option.rect;
barOpt.minimum = 0;
barOpt.maximum = 100;
barOpt.progress = num;
barOpt.text = QString::number(num) + "%";
barOpt.textVisible = true;
barOpt.textAlignment = (Qt::Alignment)index.data(Qt::TextAlignmentRole).toUInt();
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &barOpt, painter);
}
else
{
opt.text = index.data().toString();
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
}
// 绘制下边框虚线
QPen pen(painter->pen());
painter->save();
pen.setStyle(Qt::DashLine);
pen.setDashPattern(QVector<qreal>() << 1 << 1);
painter->setPen(pen);
painter->drawLine(QPointF(option.rect.left(), option.rect.bottom()), QPointF(option.rect.right(), option.rect.bottom()));
painter->restore();
}
bool MyTableDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
// 控制 check 列勾选功能
if (index.column() == MyTableModel::HEADER_CHECK
&& event->type() == QEvent::MouseButtonRelease)
{
model->setData(index, !index.data().toBool());
event->accept();
return true;
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
QWidget* MyTableDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// 根据不同的列,创建不同的编辑器
if (index.column() == MyTableModel::HEADER_SEX)
{
QComboBox* box = new QComboBox(parent);
box->addItems(QStringList() << "女" << "男");
return box;
}
else if (index.column() == MyTableModel::HEADER_AGE)
{
QSpinBox* box = new QSpinBox(parent);
box->setMinimum(0);
return box;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void MyTableDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// 指示编辑器的位置和大小
if (editor != nullptr)
{
editor->setGeometry(option.rect);
}
}
void MyTableDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
if (index.column() == MyTableModel::HEADER_SEX)
{
QComboBox* box = static_cast<QComboBox*>(editor);
model->setData(index, box->currentIndex());
return;
}
else if (index.column() == MyTableModel::HEADER_AGE)
{
QSpinBox* box = static_cast<QSpinBox*>(editor);
model->setData(index, box->value());
return;
}
return QStyledItemDelegate::setModelData(editor, model, index);
}
void MyTableDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (index.column() == MyTableModel::HEADER_SEX)
{
QComboBox* box = static_cast<QComboBox*>(editor);
box->setCurrentIndex(index.data().toInt() > 0 ? 1 : 0);
}
else if (index.column() == MyTableModel::HEADER_AGE)
{
QSpinBox* box = static_cast<QSpinBox*>(editor);
box->setValue(index.data().toInt());
}
else
{
QLineEdit* edit = static_cast<QLineEdit*>(editor);
if (edit != nullptr)
{
edit->setText(index.data().toString());
}
}
}
完整源码链接: