Table of Contents:

自定义委托(Delegate)的全面能力解析

在 Qt 中,自定义委托(Delegate)是控制视图项(Item)呈现和行为的强大工具。除了设置高度外,自定义委托还可以实现以下功能:

1. 完全自定义绘制(paint)

void WaveItemDelegate::paint(QPainter *painter, 
                            const QStyleOptionViewItem &option, 
                            const QModelIndex &index) const {
    // 保存原始状态
    painter->save();
    
    // 设置背景
    if (option.state & QStyle::State_Selected) {
        painter->fillRect(option.rect, QColor("#e6f7ff")); // 选中状态
    } else if (option.state & QStyle::State_MouseOver) {
        painter->fillRect(option.rect, QColor("#f5f5f5")); // 悬停状态
    } else {
        painter->fillRect(option.rect, Qt::white); // 默认状态
    }
    
    // 绘制边框
    painter->setPen(QColor("#e0e0e0"));
    painter->drawRect(option.rect.adjusted(0, 0, -1, -1));
    
    // 获取数据
    MuItemData data = index.data(Qt::UserRole+1).value<MuItemData>();
    
    // 绘制文件名
    painter->setPen(Qt::black);
    QFont font = painter->font();
    font.setBold(true);
    painter->setFont(font);
    painter->drawText(option.rect.adjusted(10, 5, -10, -30), 
                     Qt::AlignLeft | Qt::AlignTop, 
                     data.filename);
    
    // 绘制波形预览
    painter->setPen(QColor("#1890ff"));
    QRect waveRect = option.rect.adjusted(10, 30, -10, -10);
    drawWavePreview(painter, waveRect, data.waveData);
    
    // 绘制时长信息
    painter->setPen(Qt::darkGray);
    painter->setFont(QFont("Arial", 8));
    painter->drawText(waveRect, Qt::AlignRight | Qt::AlignBottom, 
                     formatDuration(data.duration));
    
    // 恢复原始状态
    painter->restore();
}

2. 交互控制

bool WaveItemDelegate::editorEvent(QEvent *event, 
                                  QAbstractItemModel *model, 
                                  const QStyleOptionViewItem &option, 
                                  const QModelIndex &index) {
    if (event->type() == QEvent::MouseButtonPress) {
        QMouseEvent *me = static_cast<QMouseEvent*>(event);
        if (me->button() == Qt::LeftButton) {
            // 检测是否点击了删除按钮区域
            QRect deleteButton = calculateDeleteButtonRect(option.rect);
            if (deleteButton.contains(me->pos())) {
                emit deleteRequested(index);
                return true;
            }
        }
    }
    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

3. 编辑器创建与管理

// 创建自定义编辑器
QWidget* WaveItemDelegate::createEditor(QWidget *parent, 
                                       const QStyleOptionViewItem &option, 
                                       const QModelIndex &index) const {
    WaveItemEditor *editor = new WaveItemEditor(parent);
    // 设置编辑器属性...
    return editor;
}

// 设置编辑器数据
void WaveItemDelegate::setEditorData(QWidget *editor, 
                                    const QModelIndex &index) const {
    WaveItemEditor *customEditor = static_cast<WaveItemEditor*>(editor);
    MuItemData data = index.data(Qt::UserRole+1).value<MuItemData>();
    customEditor->setData(data);
}

// 保存数据到模型
void WaveItemDelegate::setModelData(QWidget *editor, 
                                   QAbstractItemModel *model, 
                                   const QModelIndex &index) const {
    WaveItemEditor *customEditor = static_cast<WaveItemEditor*>(editor);
    MuItemData newData = customEditor->getData();
    model->setData(index, QVariant::fromValue(newData), Qt::UserRole+1);
}

4. 动画效果

void WaveItemDelegate::paint(QPainter *painter, 
                            const QStyleOptionViewItem &option, 
                            const QModelIndex &index) const {
    // ...
    
    // 绘制加载动画
    if (data.isLoading) {
        int progress = calculateProgress();
        drawLoadingAnimation(painter, option.rect, progress);
    }
    
    // 绘制交互反馈
    if (m_hoverIndex == index) {
        drawHoverEffect(painter, option.rect);
    }
}

5. 复杂布局

QSize WaveItemDelegate::sizeHint(const QStyleOptionViewItem &option, 
                                const QModelIndex &index) const {
    MuItemData data = index.data(Qt::UserRole+1).value<MuItemData>();
    
    // 计算所需高度
    int height = 60; // 基础高度
    if (data.hasTags) height += 20; // 如果有标签额外高度
    if (data.hasPreview) height += 30; // 如果有预览额外高度
    
    return QSize(option.rect.width(), height);
}

6. 状态管理

void WaveItemDelegate::paint(QPainter *painter, 
                            const QStyleOptionViewItem &option, 
                            const QModelIndex &index) const {
    // ...
    
    // 根据状态显示不同图标
    if (data.status == MuItemData::Processing) {
        drawIcon(painter, option.rect.topRight(), QIcon(":/processing.png"));
    } else if (data.status == MuItemData::Completed) {
        drawIcon(painter, option.rect.topRight(), QIcon(":/completed.png"));
    } else if (data.status == MuItemData::Error) {
        drawIcon(painter, option.rect.topRight(), QIcon(":/error.png"));
    }
}

7. 数据可视化

void WaveItemDelegate::drawWavePreview(QPainter *painter, 
                                      const QRect &rect, 
                                      const QVector<float> &waveData) const {
    if (waveData.isEmpty()) return;
    
    painter->save();
    QPainterPath path;
    path.moveTo(rect.left(), rect.center().y());
    
    float dx = static_cast<float>(rect.width()) / (waveData.size() - 1);
    for (int i = 0; i < waveData.size(); ++i) {
        float y = rect.center().y() - waveData[i] * rect.height() / 2;
        path.lineTo(rect.left() + i * dx, y);
    }
    
    painter->setPen(QPen(QColor("#1890ff"), 1.5));
    painter->drawPath(path);
    painter->restore();
}

8. 性能优化

// 在委托类中添加缓存
mutable QCache<QString, QPixmap> m_iconCache;

void WaveItemDelegate::drawIcon(QPainter *painter, 
                               const QPoint &pos, 
                               const QString &iconPath) const {
    // 检查缓存
    if (!m_iconCache.contains(iconPath)) {
        QPixmap icon(iconPath);
        icon = icon.scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        m_iconCache.insert(iconPath, new QPixmap(icon));
    }
    
    painter->drawPixmap(pos, *m_iconCache.object(iconPath));
}

9. 自定义控件集成

void WaveItemDelegate::paint(QPainter *painter, 
                            const QStyleOptionViewItem &option, 
                            const QModelIndex &index) const {
    // ...
    
    // 绘制播放按钮
    if (data.canPlay) {
        QRect playRect = calculatePlayButtonRect(option.rect);
        drawPlayButton(painter, playRect, data.isPlaying);
    }
    
    // 绘制音量控制
    if (data.showVolumeControl) {
        QRect volumeRect = calculateVolumeRect(option.rect);
        drawVolumeControl(painter, volumeRect, data.volume);
    }
}

10. 高级样式控制

void WaveItemDelegate::paint(QPainter *painter, 
                            const QStyleOptionViewItem &option, 
                            const QModelIndex &index) const {
    // ...
    
    // 根据主题设置颜色
    QColor textColor = m_theme == DarkTheme ? Qt::white : Qt::black;
    QColor bgColor = m_theme == DarkTheme ? QColor("#333") : Qt::white;
    
    // 绘制特殊状态效果
    if (data.isModified) {
        painter->setPen(QPen(Qt::yellow, 2));
        painter->drawRect(option.rect.adjusted(1, 1, -1, -1));
    }
}

完整自定义委托示例

class AdvancedWaveDelegate : public QStyledItemDelegate {
    Q_OBJECT
public:
    explicit AdvancedWaveDelegate(QObject *parent = nullptr);
    
    QSize sizeHint(const QStyleOptionViewItem &option, 
                  const QModelIndex &index) const override;
    
    void paint(QPainter *painter, 
               const QStyleOptionViewItem &option, 
               const QModelIndex &index) const override;
    
    bool editorEvent(QEvent *event, 
                    QAbstractItemModel *model, 
                    const QStyleOptionViewItem &option, 
                    const QModelIndex &index) override;
    
signals:
    void playClicked(const QModelIndex &index);
    void deleteClicked(const QModelIndex &index);
    void volumeChanged(const QModelIndex &index, int volume);
    
private:
    void drawWavePreview(QPainter *painter, const QRect &rect, 
                        const QVector<float> &waveData) const;
    void drawPlayButton(QPainter *painter, const QRect &rect, 
                       bool isPlaying) const;
    void drawVolumeControl(QPainter *painter, const QRect &rect, 
                          int volume) const;
    QRect playButtonRect(const QRect &itemRect) const;
    QRect deleteButtonRect(const QRect &itemRect) const;
    QRect volumeControlRect(const QRect &itemRect) const;
    
    mutable QModelIndex m_hoverIndex;
    int m_activeVolumeControl = -1; // 当前激活的音量控制项
};

// 实现略... 包含上述所有功能

何时使用自定义委托

场景 推荐方法
简单高度调整 使用sizeHint()
复杂视觉效果 完全重写paint()
项内交互 实现editorEvent()
自定义编辑 实现createEditor()系列方法
性能敏感场景 使用缓存和部分重绘优化

最佳实践建议

  1. 性能优先:对于大型列表,避免在paint()中进行复杂计算
  2. 状态管理:使用模型存储持久状态,委托只负责显示
  3. 代码复用:将绘制功能拆分为独立方法
  4. 主题支持:通过配置变量支持多种主题
  5. 响应式设计:确保委托适应不同尺寸
  6. 交互反馈:提供清晰的视觉反馈
  7. 可访问性:考虑高对比度模式和屏幕阅读器支持

自定义委托是 Qt 视图系统中功能最强大的组件之一,通过合理利用其能力,可以实现几乎任何类型的列表/表格项呈现效果和交互行为。