Qt委托(Delegate)类技术详解

Qt 委托(Delegate)类概述

Qt的Delegate类用于在视图组件(如QListView、QTableView、QTreeView)中控制数据的显示和编辑方式。Delegate允许开发者自定义单元格的外观、编辑器创建和数据处理逻辑,常见子类包括QStyledItemDelegate和QItemDelegate。
主要负责:

  • 控制数据项的显示方式
  • 提供数据项的编辑功能
  • 管理模型与视图之间的数据交互

委托类继承关系:

在这里插入图片描述


核心功能与使用场景

  1. 数据展示定制
    重写paint()方法可自定义单元格的绘制逻辑,例如显示进度条、图标组合或特殊格式文本。

  2. 数据编辑控制
    通过createEditor()setEditorData()setModelData()实现编辑器的创建、数据同步与回写。

  3. 交互增强
    editorEvent()中处理鼠标/键盘事件,支持复杂交互如双击触发二级对话框。


常用子类对比

QStyledItemDelegate

  • 推荐使用的默认委托,支持样式表(QSS)。
  • 自动处理标准数据类型(如颜色、字体)的编辑控件。
  • 示例代码(简单的星级评分委托):
#include <QStyledItemDelegate>
#include <QPainter>

class StarDelegate : public QStyledItemDelegate {
    Q_OBJECT
public:
    explicit StarDelegate(QObject *parent = nullptr) 
        : QStyledItemDelegate(parent) {}
    
    // 重写绘制方法
    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const override {
        if (index.data().canConvert<int>()) {
            int rating = index.data().toInt();
            
            painter->save();
            painter->setRenderHint(QPainter::Antialiasing, true);
            
            // 绘制背景
            if (option.state & QStyle::State_Selected) {
                painter->fillRect(option.rect, option.palette.highlight());
            }
            
            // 绘制星星
            QRect starRect = option.rect.adjusted(2, 2, -2, -2);
            starRect.setWidth(starRect.height()); // 保持正方形
            
            for (int i = 0; i < 5; ++i) {
                if (i < rating) {
                    painter->setBrush(Qt::yellow);
                    painter->setPen(Qt::black);
                } else {
                    painter->setBrush(Qt::NoBrush);
                    painter->setPen(Qt::gray);
                }
                
                painter->drawPolygon(createStar(starRect));
                starRect.translate(starRect.width() + 2, 0);
            }
            
            painter->restore();
        } else {
            QStyledItemDelegate::paint(painter, option, index);
        }
    }
    
    // 重写大小计算
    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const override {
        return QSize(5 * 20, 20); // 5颗星,每颗20px宽
    }

private:
    // 创建五角星形状
    QPolygonF createStar(const QRect &rect) const {
        QPolygonF star;
        qreal radius = rect.height() / 2;
        qreal centerX = rect.left() + radius;
        qreal centerY = rect.top() + radius;
        
        for (int i = 0; i < 5; ++i) {
            qreal angle = 2 * M_PI * i / 5 - M_PI / 2;
            star << QPointF(centerX + radius * cos(angle),
                           centerY + radius * sin(angle));
            
            angle += M_PI / 5;
            star << QPointF(centerX + radius * 0.5 * cos(angle),
                           centerY + radius * 0.5 * sin(angle));
        }
        
        return star;
    }
};

QItemDelegate

  • 旧版实现,不依赖样式系统。
  • 需要手动处理更多绘图细节,适用于特殊兼容需求。

实现自定义委托的关键步骤

1. 继承QStyledItemDelegate
创建子类并重写必要方法:

class StarRatingDelegate : public QStyledItemDelegate {
    Q_OBJECT
public:
    void paint(QPainter *painter, /*...*/) const override;
    QSize sizeHint(/*...*/) const override;
    QWidget *createEditor(/*...*/) const override;
};

2. 实现绘图逻辑
paint()中利用QPainter绘制自定义内容:

void StarRatingDelegate::paint(QPainter *painter, /*...*/) const {
    int stars = index.data().toInt();
    QRect rect = option.rect.adjusted(2, 2, -2, -2);
    painter->drawRect(rect);
    painter->fillRect(rect, QColor(stars * 50, 0, 0)); // 示例:根据值变色
}

3. 创建编辑器控件
返回适合数据类型的编辑组件(如QSpinBox、QComboBox):

QWidget *createEditor(QWidget *parent, /*...*/) const override {
    QSpinBox *editor = new QSpinBox(parent);
    editor->setRange(0, 5);
    return editor;
}

4. 数据同步处理
双向绑定模型与编辑器数据:

void setEditorData(QWidget *editor, /*...*/) const override {
    QSpinBox *box = static_cast<QSpinBox*>(editor);
    box->setValue(index.data().toInt());
}

void setModelData(QWidget *editor, /*...*/) const override {
    QSpinBox *box = static_cast<QSpinBox*>(editor);
    model->setData(index, box->value());
}

高级应用技巧

动态样式调整
通过initStyleOption()修改基础样式选项,再调用父类绘制:

void paint(QPainter *painter, /*...*/) const override {
    QStyleOptionViewItem opt = option;
    if (index.row() % 2 == 0) 
        opt.font.setBold(true);
    QStyledItemDelegate::paint(paint, opt, index);
}

编辑器事件过滤
eventFilter()中捕获编辑器特定事件(如回车确认编辑)。

性能优化
对于大型数据集,避免在paint()中进行复杂计算,优先使用模型角色(如Qt::DecorationRole)预处理数据。

条件委托

class ConditionalDelegate : public QStyledItemDelegate {
public:
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                         const QModelIndex &index) const override {
        if (index.column() == 0) { // 第一列使用QLineEdit
            return new QLineEdit(parent);
        } 
        else if (index.data().canConvert<double>()) { // 数值使用QSpinBox
            QSpinBox *editor = new QSpinBox(parent);
            editor->setRange(0, 100);
            return editor;
        }
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
    
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                     const QModelIndex &index) const override {
        if (index.column() == 0 && qobject_cast<QLineEdit*>(editor)) {
            model->setData(index, static_cast<QLineEdit*>(editor)->text());
        }
        else if (qobject_cast<QSpinBox*>(editor)) {
            model->setData(index, static_cast<QSpinBox*>(editor)->value());
        }
        else {
            QStyledItemDelegate::setModelData(editor, model, index);
        }
    }
};

委托开发的最佳实践

1、性能优化:

  • 在paint()方法中保存和恢复painter状态
  • 避免在paint()中创建临时对象
  • 对于复杂绘制,考虑缓存绘制结果

2、可访问性:

  • 确保委托支持高DPI显示
  • 考虑颜色对比度以满足可访问性要求

3、一致性:

  • 继承QStyledItemDelegate而不是QAbstractItemDelegate
  • 使用QStyle绘制标准UI元素

4、编辑体验:

  • 在编辑器中使用合适的验证器
  • 提供清晰的编辑反馈

委托开发实战演示

1、效果展示

在这里插入图片描述

2、开发流程

a、新建工程

在这里插入图片描述

b、添加MStarDelegate

.hpp

#ifndef MSTARDELEGATE_H
#define MSTARDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QSpinBox>

class MStarDelegate: public QStyledItemDelegate
{
    Q_OBJECT
public:
    MStarDelegate(QObject *parent = nullptr);
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

#endif // MSTARDELEGATE_H

.cpp

#include "mstardelegate.h"

MStarDelegate::MStarDelegate(QObject *parent):
    QStyledItemDelegate(parent)
{

}

void MStarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QPointF vertex[5]; /* 五角星 Size=20,20 */
    for (int i = 0; i < 5; i++)
    {
        vertex[i].setX(+10 * sinf(i * 144.0f * (3.1415926535f / 180.0f)));
        vertex[i].setY(-10 * cosf(i * 144.0f * (3.1415926535f / 180.0f)));
    }
    int many = index.data(Qt::DisplayRole).toInt();
    painter->setPen(Qt::NoPen);
    painter->setBrush(QColor(255, 170, 0));
    QSize iconSize = option.decorationSize;
    QRect thisRect = option.rect;
    int y = thisRect.y() + thisRect.height() / 2;
    for (int i = 0; i < many; i++)
    {
        painter->save();
        painter->setRenderHint(QPainter::Antialiasing);
        qreal left = 2 + iconSize.width() * 0.5 + (2 + iconSize.width()) * i;
        painter->translate(left, y);
        painter->scale(iconSize.width() / 20.0, iconSize.height() / 20.0);
        painter->drawPolygon(vertex, 5, Qt::WindingFill);
        painter->restore();
    }
    // if (option.state & QStyle::State_Selected)
    // {
    //     painter->fillRect(option.rect, option.palette.highlight());
    // }
}

QWidget *MStarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSpinBox* cbBox = new QSpinBox(parent);
    cbBox->setRange(0, 5);
    cbBox->setSuffix(u8"星");
    return cbBox;
}

void MStarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QSpinBox* cbWidget = dynamic_cast<QSpinBox*>(editor);
    cbWidget->setValue(index.data(Qt::DisplayRole).toInt());
}

void MStarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    int level = dynamic_cast<QSpinBox*>(editor)->value();
    model->setData(index, level, Qt::DisplayRole);
}

void MStarDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
}

c、工程结构

在这里插入图片描述

d、放置TableView

在这里插入图片描述

e、设置委托

    this->ui->tableView->setItemDelegateForColumn(0, new MStarDelegate(this->ui->tableView));
    QStandardItemModel* model = new QStandardItemModel(this->ui->tableView);
    model->setHorizontalHeaderLabels({ u8"点数", u8"状态" });
    model->setVerticalHeaderLabels({ "哪吒", "孙悟空", "猪八戒", "唐僧" });
    model->setColumnCount(2);
    model->setRowCount(4);
    for (int i = 0; i < 4; i++)
    {
        model->setItem(i, 0, new QStandardItem(QString::number(i)));
        model->setItem(i, 1, new QStandardItem(QString("剩余%1点血").arg(i)));
    }
    this->ui->tableView->setModel(model);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小灰灰搞电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值