参考:
Qt 自定义委托_w3cschool
https://www.w3cschool.cn/learnroadqt/ov8h1j4z.html
C++ GUI Programming with Qt 4, Second Edition
本地环境:
win10专业版,64位,Qt 5.12
理论知识
Qt的model/view架构中,view只是1.对model数据的展示 2.处理用户输入。但是view只是讲用户输入委托给delegate处理
在Qt中,委托(Delegate)是一种用于控制如何显示和编辑视图中的数据的机制。它允许我们:对于不同的单元格,可以:
- 自定义不同的外观,比如使用自定义的图标、颜色或对齐方式
- 自定义不同的行为,比如为某些单元格提供特殊的编辑器,比如自定义的下拉列表、输入控件
委托和视图之间的区别在于:
- 视图负责显示和编辑数据,而委托负责定义和实现如何显示和编辑特定单元格。
- 视图是整个可见区域,如
QTableView
或QTreeView
,而委托是针对特定单元格的。
Qt 4.4版本之后提供了两个可以被继承的 delegate 类:QItemDelegate
和 QStyledItemDelegate
。默认的 delegate 是 QStyledItemDelegate
。
这两个类可以被相互替代,主要区别在于,QStyledItemDelegate
使用当前的风格(style)去绘制组件。所以,在自定义 delegate 或者需要使用 Qt style sheets 时,建议使用 QStyledItemDelegate
作为父类。使用这两个类的代码通常是一样的,除了需要使用 style进行绘制的部份。如果你希望为 view item 自定义绘制函数,最好实现一个自定义的 style。这个你可以通过 QStyle 类来实现。
如果 delegate 没有支持为你的数据类型进行绘制,或者你希望自己绘制 item,那么就可以继承 QStyledItemDelegate
类,并且重写 paint() 或者还需要重写 sizeHint() 函数。paint() 函数会被每一个 item 独立调用,而 sizeHint()函数则可以定义每一个 item 的大小。在重写 paint() 函数的时候,通常需要用 if 语句找到你需要进行渲染的数据类型并进行绘制,其他的数据类型需要调用父类的实现进行绘制。
一个自定义的 delegate 也可以直接提供一个编辑器,而不是使用内置的编辑器工厂(editor item factory)。如果你需要这种功能,那么需要实现一下几个函数:
- createEditor():返回修改数据的组件;
- setEditorData():为 editor 提供编辑的原始数据;
- updateEditorGeometry():保证 editor 显示在 item view 的合适位置以及大小;
- setModelData():根据 editor 的数据更新 model 的数据。
实现效果
假设有一个table,每个单元格显示的是当前数据的平方值(原始数据是1~5)
双击单元格,可以编辑,编辑完成后立刻显示。
实现
model
#ifndef CUSTOMTABLEMODEL_H
#define CUSTOMTABLEMODEL_H
#include <QAbstractTableModel>
class CustomTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit CustomTableModel(QObject* parent = nullptr)
: QAbstractTableModel(parent)
{
// 初始化数据
m_data << 1 << 2 << 3 << 4 << 5;
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return m_data.size();
}
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
return 1;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole) {
return m_data.at(index.row());
}
return QVariant();
}
Qt::ItemFlags flags(const QModelIndex& index) const override
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override
{
if (index.isValid() && role == Qt::EditRole) {
m_data.replace(index.row(), value.toInt());
emit dataChanged(index, index);
return true;
}
return false;
}
private:
QList<int> m_data;
};
#endif // CUSTOMTABLEMODEL_H
自定义委托
#ifndef SQUAREDELEGATE_H
#define SQUAREDELEGATE_H
#include <QStyledItemDelegate>
#include <QModelIndex>
#include <QObject>
#include <QSize>
#include <QSpinBox>
#include <QPainter>
class SquareDelegate : public QStyledItemDelegate
{
public:
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
return QStyledItemDelegate::sizeHint(option, index);
}
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
if (index.isValid()) {
int value = index.model()->data(index).toInt();
QString text = QString::number(value * value);
painter->save();
painter->drawText(option.rect, Qt::AlignCenter, text);
painter->restore();
} else {
// 这是绘制原来的数据。如果原数据和平方值都要显示,可以把这句放在最前面
QStyledItemDelegate::paint(painter, option, index);
}
}
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
QSpinBox* editor = new QSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
void setEditorData(QWidget* editor, const QModelIndex& index) const override
{
int value = index.model()->data(index).toInt();
QSpinBox* spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override
{
/*
QSpinBox* spinBox = static_cast<QSpinBox*>(editor);
int value = spinBox->value();
model->setData(index, value);
*/
if (QSpinBox* spinBox = qobject_cast<QSpinBox*>(editor)) {
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
} else {
QStyledItemDelegate::setModelData(editor, model, index);
}
}
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
editor->setGeometry(option.rect);
}
};
#endif // SQUAREDELEGATE_H
main
QTableView* tableView = new QTableView;
SquareDelegate* delegate = new SquareDelegate;
// 设置委托
tableView->setItemDelegate(delegate);
CustomTableModel* model = new CustomTableModel;
// 设置模型
tableView->setModel(model);
tableView->show();