Qt 中的自定义代理(QStyledItemDelegateQItemDelegate)用于控制模型视图结构中的单元格的显示和编辑方式。代理可以自定义单元格的绘制、数据呈现和编辑器的行为。通过自定义代理,你可以创建不同于默认控件的外观和交互方式,使得视图更加灵活和个性化。

1. QStyledItemDelegateQItemDelegate

  • QStyledItemDelegate:是 QItemDelegate 的改进版,推荐使用。它提供了更好的样式支持,能够与平台的外观和感觉保持一致。
  • QItemDelegate:是较早的代理类,功能和 QStyledItemDelegate 类似,但处理样式时不如 QStyledItemDelegate 先进。为了跨平台一致性,通常建议使用 QStyledItemDelegate

2. 代理的主要功能

自定义代理主要用于两种场景:

  • 自定义绘制:在显示数据时定制单元格的外观,比如绘制复杂图形、文本布局等。
  • 自定义编辑器:提供自定义的输入控件(如使用下拉框、颜色选择器等)来编辑单元格数据。

3. 自定义代理的常用方法

要实现一个自定义代理类,需要继承 QStyledItemDelegate 并重写一些方法:

1. paint()

paint() 方法用于定制单元格的绘制。它允许你控制如何在视图中绘制每个单元格的内容。

函数签名:

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

功能:

  • painter:用于绘制单元格内容。
  • option:提供关于单元格样式的信息(如单元格是否被选中、焦点等)。
  • index:提供模型中的数据和单元格的位置。

通过自定义 paint(),你可以绘制自定义的图形、颜色、背景、文本布局等。

示例:

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
    // 获取数据
    QString text = index.model()->data(index, Qt::DisplayRole).toString();
    
    // 设置背景颜色
    painter->fillRect(option.rect, option.state & QStyle::State_Selected ? Qt::blue : Qt::white);
    
    // 绘制文本
    painter->drawText(option.rect, Qt::AlignCenter, text);
}
2. createEditor()

createEditor() 用于创建单元格的自定义编辑器。当用户双击单元格或触发编辑行为时,视图会调用这个方法来生成编辑器控件。

函数签名:

QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

功能:

  • parent:新编辑器的父窗口。
  • option:提供单元格的样式信息。
  • index:提供被编辑单元格的模型索引。

你可以在此方法中返回任意一个 QWidget 作为编辑器,比如下拉框、颜色选择器、滑动条等。

示例:

QWidget* MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
    // 创建一个自定义的 QComboBox 作为编辑器
    QComboBox *editor = new QComboBox(parent);
    editor->addItems({"Option 1", "Option 2", "Option 3"});
    return editor;
}
3. setEditorData()

setEditorData() 用于将模型中的数据加载到编辑器中。视图调用这个方法来初始化编辑器控件的内容。

函数签名:

void setEditorData(QWidget *editor, const QModelIndex &index) const override;

功能:

  • editor:要初始化的编辑器。
  • index:表示被编辑单元格的模型索引,通过它获取数据。

你可以根据模型的数据类型来初始化编辑器的状态。

示例:

void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
    // 将模型中的文本设置到 QComboBox 中
    QString value = index.model()->data(index, Qt::EditRole).toString();
    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    comboBox->setCurrentText(value);
}
4. setModelData()

setModelData() 用于将编辑器中的数据提交到模型中。用户结束编辑时,视图会调用此方法更新模型中的数据。

函数签名:

void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;

功能:

  • editor:编辑器控件。
  • model:要更新的数据模型。
  • index:表示被编辑单元格的模型索引。

示例:

void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
    // 获取 QComboBox 的当前选择,并将其写入模型
    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    QString value = comboBox->currentText();
    model->setData(index, value, Qt::EditRole);
}
5. updateEditorGeometry()

updateEditorGeometry() 用于调整编辑器控件的大小和位置。你可以通过这个方法确保编辑器正确地放置在单元格内。

函数签名:

void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

功能:

  • editor:要调整的编辑器控件。
  • option:提供单元格的样式信息(包括大小、位置等)。
  • index:表示单元格的模型索引。

示例:

void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
    editor->setGeometry(option.rect);  // 将编辑器放置到单元格内
}

4. 自定义代理的完整示例

下面是一个完整的自定义代理的示例,它实现了在表格中使用下拉框作为编辑器,并自定义单元格的绘制。

class MyDelegate : public QStyledItemDelegate {
public:
    MyDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}

    // 自定义单元格绘制
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QString text = index.model()->data(index, Qt::DisplayRole).toString();
        painter->fillRect(option.rect, option.state & QStyle::State_Selected ? Qt::yellow : Qt::white);
        painter->drawText(option.rect, Qt::AlignCenter, text);
    }

    // 创建自定义编辑器
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QComboBox *editor = new QComboBox(parent);
        editor->addItems({"Apple", "Banana", "Cherry"});
        return editor;
    }

    // 初始化编辑器数据
    void setEditorData(QWidget *editor, const QModelIndex &index) const override {
        QString value = index.model()->data(index, Qt::EditRole).toString();
        QComboBox *comboBox = static_cast<QComboBox*>(editor);
        comboBox->setCurrentText(value);
    }

    // 提交编辑器数据到模型
    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
        QComboBox *comboBox = static_cast<QComboBox*>(editor);
        QString value = comboBox->currentText();
        model->setData(index, value, Qt::EditRole);
    }

    // 调整编辑器位置
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        editor->setGeometry(option.rect);
    }
};

5. 总结

自定义代理通过重写 QStyledItemDelegate 的方法,可以灵活地控制单元格的显示和编辑行为。通过 paint() 方法定制单元格的绘制,通过 createEditor() 和其他相关方法控制单元格编辑器的行为,从而实现个性化的用户体验。

代理提供了一种强大的方式来定制 Qt 的模型视图框架,使其适应特定的需求和外观要求。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐