模型视图中的委托

问题

模型负责组织数据,视图负责显示数据,如何编辑修改数据?

传统的 MVC 设计模式

Qt 中的模型视图设计模式如何处理用户输入?

视图中集成了处理用户输入的功能

视图将用户输入作为内部的子功能而实现

模型视图中的委托

委托 (Delegate) 是视图中处理用户输入的部件

视图可以设置委托对象用于处理用户输入

委托对象负责创建和显示用户上下文

  • 如:编辑框的创建和显示

委托中的编辑器

委托能够提供编辑时需要的上下文环境 (编辑器)

不同委托提供的编辑器类型不同 (文本框,单选框,等)

编辑器从模型获取数据,并将编辑结果返回模型

委托中的关键函数 

createEditor 

  • 需要编辑数据时,创建编辑器组件

updateEditorGeometry

  • 更新编辑器组件的大小

setEditorData

  • 通过索引从模型中获取数据

setModelData

  • 将编辑后的新数据返回模型

委托中的关键信号

void closeEditor(QWidget* editor)

  • 编辑器组件关闭信号

void commitData(QWidget* editor)

  • 新数据提交信号

委托的关键函数和信号

SubStyledItemDelegate.h

#ifndef SUBSTYLEDITEMDELEGATE_H
#define SUBSTYLEDITEMDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>

class SubStyledItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
private slots:

    void OnCloseEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint = NoHint);
    void onCommitData(QWidget* editor);

public:
    explicit SubStyledItemDelegate(QObject* parent = nullptr);

    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index);
    void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const;
    void setEditorData(QWidget* editor, const QModelIndex& index) const;
    void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index);

signals:

};

#endif // SUBSTYLEDITEMDELEGATE_H

SubStyledItemDelegate.cpp

#include "SubStyledItemDelegate.h"
#include <QDebug>

SubStyledItemDelegate::SubStyledItemDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
    connect(this, &QStyledItemDelegate::closeEditor, this, &SubStyledItemDelegate::OnCloseEditor);
    connect(this, &QStyledItemDelegate::commitData, this, &SubStyledItemDelegate::onCommitData);
}

QWidget* SubStyledItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    qDebug() << "SubStyledItemDelegate::createEditor";

    return QStyledItemDelegate::createEditor(parent, option, index);
}

void SubStyledItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
    qDebug() << "SubStyledItemDelegate::updateEditorGeometry";

    return QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}

void SubStyledItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    qDebug() << "SubStyledItemDelegate::setEditorData";

    return QStyledItemDelegate::setEditorData(editor, index);
}

void SubStyledItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index)
{
    qDebug() << "SubStyledItemDelegate::setModelData";

    return QStyledItemDelegate::setModelData(editor, model, index);
}

void SubStyledItemDelegate::OnCloseEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint)
{
    qDebug() << "SubStyledItemDelegate::OnCloseEditor";
}

void SubStyledItemDelegate::onCommitData(QWidget* editor)
{
    qDebug() << "SubStyledItemDelegate::onCommitData";
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTableView>
#include <QPushButton>
#include <QStandardItemModel>
#include "SubStyledItemDelegate.h"

class Widget : public QWidget
{
    Q_OBJECT
private:
    QStandardItemModel m_model;
    QTableView m_view;
    QPushButton m_testBtn;
    SubStyledItemDelegate m_delegate;

    void initModel();
    void initView();

private slots:
    void onTestBtnClicked();

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QStandardItem>
#include <QModelIndex>
#include <QDebug>

Widget::Widget(QWidget* parent)
    : QWidget(parent)
{
    m_testBtn.setParent(this);
    m_testBtn.move(10, 120);
    m_testBtn.resize(300, 30);
    m_testBtn.setText("test");

    initModel();
    initView();
    m_view.setModel(&m_model);

    connect(&m_testBtn, &QPushButton::clicked, this, &Widget::onTestBtnClicked);
}

void Widget::initModel()
{
    QStandardItem *root = m_model.invisibleRootItem();
    QStandardItem *itemA = new QStandardItem();
    QStandardItem *itemB = new QStandardItem();
    QStandardItem *itemC = new QStandardItem();
    QStandardItem *itemD = new QStandardItem();

    itemA->setData("A", Qt::DisplayRole);
    itemB->setData("B", Qt::DisplayRole);
    itemC->setData("C", Qt::DisplayRole);
    itemD->setData("D", Qt::DisplayRole);

    root->setChild(0, 0, itemA);
    root->setChild(0, 1, itemB);
    root->setChild(1, 0, itemC);
    root->setChild(1, 1, itemD);
}

void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(300, 100);
    m_view.setItemDelegate(&m_delegate);
};

void Widget::onTestBtnClicked()
{
    for(int i = 0; i < m_model.rowCount(); i++)
    {
        qDebug() << "row : " << i;

        for(int j = 0; j < m_model.columnCount(); j++)
        {
            QModelIndex index = m_model.index(i, j, QModelIndex());
            QString text = index.data(Qt::DisplayRole).toString();

            qDebug() << text;
        }
    }

    qDebug() << m_view.itemDelegate();
}

Widget::~Widget()
{
}

委托的本质

为视图提供编辑的上下文环境

产生界面元素的工厂类

能够使用和设置模型中的数据

问题

如何自定一个委托类?

自定义委托时需要重写的函数

1. 重写 createEditor 成员函数 

  • 根据索引中值的类型创建编辑器组件

2. 重写 updateEditorGeometry 成员函数

  • 根据参数中数据项的信息设置编辑器的位置和大小

3. 重写 setEditorData 成员函数

  • 根据参数中的数据索引设置编辑器中的内容

4. 重写 setModelData 成员函数

  • 根据参数中的数据索引更改模型中的数据

重写 paint 成员函数 (可选)

  • 根据参数中的信息绘制编辑器

自定义委托

CustomizedDelegate.h

#ifndef CustomizedDeletage_H
#define CustomizedDeletage_H

#include <QObject>
#include <QItemDelegate>
#include <QModelIndex>

class CustomizedDeletage : public QItemDelegate
{
    Q_OBJECT
protected:
    mutable QModelIndex m_index;
protected slots:
    void onCloseEditor(QWidget*);
public:
    explicit CustomizedDeletage(QObject* parent = nullptr);

    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
    void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const;
    void setEditorData(QWidget* editor, const QModelIndex& index) const;
    void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;

signals:

};

#endif // CustomizedDeletage_H

CustomizedDelegate.cpp

#include "CustomizedDeletage.h"
#include <QVariant>
#include <QCheckBox>
#include <QComboBox>

CustomizedDeletage::CustomizedDeletage(QObject* parent) : QItemDelegate(parent)
{
    connect(this, &QItemDelegate::closeEditor, this, &CustomizedDeletage::onCloseEditor);
}

QWidget* CustomizedDeletage::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QWidget* w = NULL;
    m_index = index;

    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = new QCheckBox(parent);

        cb->setText("selece to true");

        w = cb;
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = new QComboBox(parent);

        cb->addItem("A");
        cb->addItem("B");
        cb->addItem("C");
        cb->addItem("D");

        w = cb;
    }
    else
    {
        w = QItemDelegate::createEditor(parent, option, index);
    }

    return w;
}

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

void CustomizedDeletage::setEditorData(QWidget* editor, const QModelIndex &index) const
{
    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);

        if(cb != NULL)
        {
            cb->setChecked(index.data().toBool());
        }
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            for(int i = 0; i < cb->count(); i++)
            {
                if(cb->itemText(i) == index.data().toString())
                {
                    cb->setCurrentIndex(i);
                    break;
                }
            }
        }
    }
    else
    {
        QItemDelegate::setEditorData(editor, index);
    }
}

void CustomizedDeletage::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);

        if(cb != NULL)
        {
            model->setData(index, cb->isChecked(), Qt::DisplayRole);
        }
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            model->setData(index, cb->currentText(), Qt::DisplayRole);
        }
    }
    else
    {
        QItemDelegate::setModelData(editor, model, index);
    }
}

void CustomizedDeletage::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if(m_index != index)
    {
        QItemDelegate::paint(painter, option, index);
    }
}

void CustomizedDeletage::onCloseEditor(QWidget*)
{
    m_index = QModelIndex();
}

Widget.h

#include "CustomizedDeletage.h"
#include <QVariant>
#include <QCheckBox>
#include <QComboBox>

CustomizedDeletage::CustomizedDeletage(QObject* parent) : QItemDelegate(parent)
{
    connect(this, &QItemDelegate::closeEditor, this, &CustomizedDeletage::onCloseEditor);
}

QWidget* CustomizedDeletage::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QWidget* w = NULL;
    m_index = index;

    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = new QCheckBox(parent);

        cb->setText("selece to true");

        w = cb;
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = new QComboBox(parent);

        cb->addItem("A");
        cb->addItem("B");
        cb->addItem("C");
        cb->addItem("D");

        w = cb;
    }
    else
    {
        w = QItemDelegate::createEditor(parent, option, index);
    }

    return w;
}

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

void CustomizedDeletage::setEditorData(QWidget* editor, const QModelIndex &index) const
{
    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);

        if(cb != NULL)
        {
            cb->setChecked(index.data().toBool());
        }
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            for(int i = 0; i < cb->count(); i++)
            {
                if(cb->itemText(i) == index.data().toString())
                {
                    cb->setCurrentIndex(i);
                    break;
                }
            }
        }
    }
    else
    {
        QItemDelegate::setEditorData(editor, index);
    }
}

void CustomizedDeletage::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if(index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);

        if(cb != NULL)
        {
            model->setData(index, cb->isChecked(), Qt::DisplayRole);
        }
    }
    else if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            model->setData(index, cb->currentText(), Qt::DisplayRole);
        }
    }
    else
    {
        QItemDelegate::setModelData(editor, model, index);
    }
}

void CustomizedDeletage::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if(m_index != index)
    {
        QItemDelegate::paint(painter, option, index);
    }
}

void CustomizedDeletage::onCloseEditor(QWidget*)
{
    m_index = QModelIndex();
}

Widget.cpp

#include "Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    initModel();
    initView();
    m_view.setModel(&m_model);

    for(int i = 0; i < m_model.columnCount(); i++)
    {
        m_view.setColumnWidth(i, 150);
    }
}

void Widget::initModel()
{
    QStandardItem *root = m_model.invisibleRootItem();
    QStandardItem *itemA1 = new QStandardItem();
    QStandardItem *itemA2 = new QStandardItem();
    QStandardItem *itemA3 = new QStandardItem();
    QStandardItem *itemB1 = new QStandardItem();
    QStandardItem *itemB2 = new QStandardItem();
    QStandardItem *itemB3 = new QStandardItem();

    QStringList list;

    list.append("Language");
    list.append("Level");
    list.append("Script");

    m_model.setHorizontalHeaderLabels(list);

    itemA1->setData("C++", Qt::DisplayRole);
    itemA2->setData(QChar('A'), Qt::DisplayRole);
    itemA3->setData(false, Qt::DisplayRole);
    itemB1->setData("Python", Qt::DisplayRole);
    itemB2->setData(QChar('B'), Qt::DisplayRole);
    itemB3->setData(true, Qt::DisplayRole);

    root->setChild(0, 0, itemA1);
    root->setChild(0, 1, itemA2);
    root->setChild(0, 2, itemA3);
    root->setChild(1, 0, itemB1);
    root->setChild(1, 1, itemB2);
    root->setChild(1, 2, itemB3);
}

void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(600, 400);
    m_view.setItemDelegate(&m_delegate);
};

Widget::~Widget()
{
}

为了防止双击编辑器后图像有覆盖的现象,使用 m_index 变量防止图像覆盖。

问题

自定义委托时重写的函数由谁调用?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的QT视图模型委托项目案例: 假设有一个表格,其一列显示了用户的年龄,我们想要让年龄小于18岁的行以红色显示,而年龄大于等于18岁的行以绿色显示。我们可以通过使用QT视图模型委托来实现此目的。 首先,我们需要定义一个自定义的委托类,该类继承自QStyledItemDelegate类。在该类,我们将实现一个paint()函数,该函数将会在每次需要绘制表格项时被调用。 ```cpp class AgeDelegate : public QStyledItemDelegate { public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // 获取年龄值 int age = index.data(Qt::DisplayRole).toInt(); // 设置画笔颜色 if (age < 18) { painter->setPen(QColor("red")); } else { painter->setPen(QColor("green")); } // 调用基类的paint()函数进行绘制 QStyledItemDelegate::paint(painter, option, index); } }; ``` 接下来,我们需要在主窗口创建一个表格,并为其设置一个QStandardItemModel模型。然后,我们将会为该模型的第二列(即年龄列)设置我们刚刚定义的委托类。 ```cpp // 创建表格和模型 QTableView *tableView = new QTableView(this); QStandardItemModel *model = new QStandardItemModel(4, 2, this); // 设置表头 model->setHeaderData(0, Qt::Horizontal, tr("Name")); model->setHeaderData(1, Qt::Horizontal, tr("Age")); // 设置模型数据 model->setData(model->index(0, 0), "John Doe"); model->setData(model->index(0, 1), 25); model->setData(model->index(1, 0), "Jane Smith"); model->setData(model->index(1, 1), 17); model->setData(model->index(2, 0), "Bob Johnson"); model->setData(model->index(2, 1), 30); model->setData(model->index(3, 0), "Mary Williams"); model->setData(model->index(3, 1), 16); // 为模型的第二列设置委托 tableView->setItemDelegateForColumn(1, new AgeDelegate(this)); // 将模型设置给表格 tableView->setModel(model); ``` 现在,我们运行程序,就可以看到表格年龄小于18岁的行以红色显示,而年龄大于等于18岁的行以绿色显示。这就是通过QT视图模型委托实现的。 注意:本示例并不完整,只是为了演示视图模型委托的基本用法。在实际应用,您可能需要对上述代码进行进一步的修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值