视图上的委托

1. 委托的概念执行过程

  在传统的MVC设计模式中,模型Model负责数据组织,视图View负责数据显示,控制器Contorller负责与用户的输入交互。如下图:

这里写图片描述

  在Qt的模型-视图设计模式中对传统的MVC设计模式做了演变:

这里写图片描述

  视图中集成了处理用户的输入功能,将与用户输入交互功能作为视图内部独立的子功能而实现,即委托(Delegate)。委托对象负责创建/显示用户输入上下文,如编辑框的创建和显示,除此,委托作为视图的一部分,还承担了部分内容的显示,关于后者,我们放在后面说,先看委托与用户输入交互部分。委托类的关系图为:

这里写图片描述

  QAbstractItemDelegate定义了委托的功能接口:

QAbstractItemDelegate ( QObject * parent = 0 )
virtual ~QAbstractItemDelegate ()
//编辑数据时调用此函数创建编辑器组件
virtual QWidget *createEditor( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const

//编辑编辑框时产生的事件调用此函数
virtual bool    editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index )

//绘制组件
virtual void    paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const = 0

//通过索引从模型中拿数据并设置到编辑器
virtual void    setEditorData ( QWidget * editor, const QModelIndex & index ) const

//将编辑器上的数据写到模型中
virtual void    setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
virtual QSize   sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const = 0

//更新编辑器组件的大小,可能用户需要输入的文字较多,原先的大小不适用所以需要调用此函数更新组件大小
virtual void    updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const

  QItemDelegate和QStyledItemDelegate都是标准委托功能的实现类。后者是Qt4.4版本后引入的基类,视图中默认的委托是QStyledItemDelegate,也就是说即不设置视图内的委托类,默认的就是QStyledItemDelegate。建议程序员在自定义委托时最好继承自QStyledItemDelegate,因为QStyledItemDelegate使用了当前的界面风格来绘制组件,即若程序中设置了总体风格,QStyledItemDelegate就会使用这个新建的风格来绘制组件。

  下面例子用于验证委托的存在,且委托实现了与用户的交互功能:

//Widget.h
class Widget : public QWidget
{
    Q_OBJECT
    QTableView          m_view;     //表格视图
    QStandardItemModel  m_model;    //标准模型
    QPushButton         m_btn;      //按钮

    void initView();    //视图初始化函数
    void initModel();   //模型初始化函数

private slots:
    void onBtnClicked();    //按钮按下槽函数

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

//Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent), m_btn(this)
{
    initView();
    initModel();

    m_view.setModel(&m_model);

    m_btn.move(10, 120);
    m_btn.resize(300, 30);
    m_btn.setText("check");

    //连接按钮按下信号和槽
    connect(&m_btn, SIGNAL(clicked()), this, SLOT(onBtnClicked()));
}

//模型初始化函数
void Widget::initModel()
{
    QStandardItem* root = m_model.invisibleRootItem();  //虚拟根节点数据项
    QStandardItem* item_a = new QStandardItem();        //数据项a
    QStandardItem* item_b = new QStandardItem();
    QStandardItem* item_c = new QStandardItem();
    QStandardItem* item_d = new QStandardItem();

    //为每个数据项添加数据,数据角色为Qt::DisplayRole
    item_a->setData("python", Qt::DisplayRole);
    item_b->setData("c++", Qt::DisplayRole);
    item_c->setData("c", Qt::DisplayRole);
    item_d->setData("kotlin", Qt::DisplayRole);

    //将数据项加入(表格)模型
    root->setChild(0, 0, item_a);
    root->setChild(0, 1, item_b);
    root->setChild(1, 0, item_c);
    root->setChild(1, 1, item_d);
}

//视图初始化函数
void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(300, 100);

}

//按钮按下的槽函数,用于打印模型中的数据项
void Widget::onBtnClicked()
{
    qDebug() << "Model Data:";

    //遍历行
    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().toString();

            qDebug() << text;
        }
    }
}

Widget::~Widget()
{
}

  运行:

这里写图片描述

  可见,视图中确有实现与用户交互的委托类的存在,且该类将用户修改后的数据写入编辑框中,同时还会写入模型中。

  下面通过自定义继承自标准委托类QStyledItemDelegate的委托类来观察委托类的执行过程。自定义委托类需要重写QStyledItemDelegate类中的函数有:

//用户编辑视图上的数据时调用此函数创建编辑组件
QWidget *createEditor(QWidget *parent,
                        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 updateEditorGeometry(QWidget *editor,
                            const QStyleOptionViewItem &option,
                            const QModelIndex &index) const;

  另外委托中还有两个关键信号:

//新数据提交时的信号函数
void commitData(QWidget *editor);

//编辑器组件关闭时的信号函数
void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint = NoHint); 

  代码实现:

//MyQStyledItemDelegate.h
#ifndef MYQSTYLEDITEMDELEGATE_H
#define MYQSTYLEDITEMDELEGATE_H

#include <QStyledItemDelegate>

class MyQStyledItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit MyQStyledItemDelegate(QObject *parent = 0);

    //用户编辑视图上的数据时调用此函数创建编辑组件
    QWidget *createEditor(QWidget *parent,
                          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 updateEditorGeometry(QWidget *editor,
                              const QStyleOptionViewItem &option,
                              const QModelIndex &index) const;

public slots:
    //编辑器组件关闭时的槽函数
    void onCloseEditor(QWidget* editor);

    //新数据提交时的槽函数
    void onCommitData(QWidget* editor);
};

#endif // MYQSTYLEDITEMDELEGATE_H

//MyQStyledItemDelegate.cpp,重写委托中关键的函数,只是实现打印以验证程序执行流程
#include "MyQStyledItemDelegate.h"
#include <QDebug>

MyQStyledItemDelegate::MyQStyledItemDelegate(QObject *parent) :
    QStyledItemDelegate(parent)
{
    connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(onCloseEditor(QWidget*)));
    connect(this, SIGNAL(commitData(QWidget*)), this, SLOT(onCommitData(QWidget*)));
}


//用户编辑视图上的数据时调用此函数创建编辑组件
QWidget *MyQStyledItemDelegate::createEditor(QWidget *parent,
                      const QStyleOptionViewItem &option,
                      const QModelIndex &index) const
{
    qDebug() << "createEditor";

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

//通过索引从模型中拿数据并设置到编辑框
void MyQStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    qDebug() << "setEditorData";
    QStyledItemDelegate::setEditorData(editor,index);
}

//将编辑框上的数据写到模型中
void MyQStyledItemDelegate::setModelData(QWidget *editor,
                  QAbstractItemModel *model,
                  const QModelIndex &index) const
{
    qDebug() << "setModelData";
    QStyledItemDelegate::setModelData(editor, model, index);
}

//更新编辑组件大小,用户在编辑数时可能原先大小的编辑框不够使用
void MyQStyledItemDelegate::updateEditorGeometry(QWidget *editor,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const
{
    qDebug() << "updateEditorGeometry";
    QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}

//编辑器组件关闭时的槽函数
void MyQStyledItemDelegate::onCloseEditor(QWidget* editor)
{
    qDebug() << "slot: onCloseEditor";
}

//新数据提交时的槽函数
void MyQStyledItemDelegate::onCommitData(QWidget* editor)
{
    qDebug() << "slot: onCommitData";
}

//Widget.h,
class Widget : public QWidget
{
    Q_OBJECT
    QTableView          m_view;
    QStandardItemModel  m_model;
    QPushButton         m_btn;

    MyQStyledItemDelegate m_delegate;   //窗口组件中新增自定义的成员对象MyQStyledItemDelegate

    void initView();
    void initModel();

private slots:
    void onBtnClicked();

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

//Widget.cpp
void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(300, 100);

    m_view.setItemDelegate(&m_delegate);    //设置视图的委托对象为我们自定义的MyQStyledItemDelegate
}

  运行:

这里写图片描述
  由运行结果可得:
  双击编辑框时调用createEditor()、updateEditorGeometry()、setEditorData();
  编辑完毕按下回车时调用onCommitData()槽函数、setModelData()、setEditorData()、onCloseEditor槽函数()。显然符合注释中所写的函数的意义。

2. 自定义委托类实现自定义用于交互的编辑组件

  通过前面知道视图中的委托为视图提供了数据编辑的上下文,在用户需要编辑视图上的数据(进而写入模型)时,需要为用于提供一个输入的编辑框,委托就是这么一个通过前面知道视图中的委托为视图提供了数据编辑的上下文,在用户需要编辑视图上的数据(进而写入模型)时,需要为用于提供一个输入的编辑框,委托就是这么一个

  视图使用默认的QStyledItemDelegate委托类:

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QModelIndex>
#include "MyQStyledItemDelegate.h"

class Widget : public QWidget
{
    Q_OBJECT
    QTableView          m_view;
    QStandardItemModel  m_model;
    QPushButton         m_btn;

    void initView();
    void initModel();

private slots:
    void onBtnClicked();

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

//Widget.cpp
#include "Widget.h"
#include <QDebug>

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

    //设置每个模型数据项占据显示框的大小
    for(int i=0; i<m_model.columnCount(); i++)
    {
        m_view.setColumnWidth(i, 125);
    }

    m_btn.move(10, 120);
    m_btn.resize(400, 30);
    m_btn.setText("check");

    connect(&m_btn, SIGNAL(clicked()), this, SLOT(onBtnClicked()));
}

void Widget::initModel()
{
    //填充模型
    QStandardItem* root = m_model.invisibleRootItem();
    QStandardItem* item_a1 = new QStandardItem();
    QStandardItem* item_b1 = new QStandardItem();
    QStandardItem* item_c1 = new QStandardItem();

    QStandardItem* item_a2 = new QStandardItem();
    QStandardItem* item_b2 = new QStandardItem();
    QStandardItem* item_c2 = new QStandardItem();

    QStringList h;

    item_a1->setData("c++", Qt::DisplayRole);
    item_b1->setData(QChar('A'), Qt::DisplayRole);
    item_c1->setData(false, Qt::DisplayRole);

    item_a2->setData("ruby", Qt::DisplayRole);
    item_b2->setData(QChar('B'), Qt::DisplayRole);
    item_c2->setData(true, Qt::DisplayRole);

    //设置表格的标题
    h.append("Language");
    h.append("Level");
    h.append("Script");
    m_model.setHorizontalHeaderLabels(h);

    root->setChild(0, 0, item_a1);
    root->setChild(0, 1, item_b1);
    root->setChild(0, 2, item_c1);

    root->setChild(1, 0, item_a2);
    root->setChild(1, 1, item_b2);
    root->setChild(1, 2, item_c2);    
}

void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(400, 100);
}

void Widget::onBtnClicked()
{
    qDebug() << "Model Data:";
    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().toString();

            qDebug() << text;
        }

        //qDebug() << endl;
    }
}

Widget::~Widget()
{
}

  运行:

这里写图片描述

  默认的委托,响应用户输入时,对bool类型的数据项采用下拉框,字符/字符串类型采用的是输入框,现在重写委托,让bool类型数据项采用复选框,而字符型采用下拉选项:

//m_model.setHorizontalHeaderLabels.h
#ifndef MYQSTYLEDITEMDELEGATE_H
#define MYQSTYLEDITEMDELEGATE_H

#include <QStyledItemDelegate>
#include <QModelIndex>

class MyQStyledItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT

    mutable QModelIndex m_index;    //记录当前操作的数据项的索引

public:
    explicit MyQStyledItemDelegate(QObject *parent = 0);

    //用户编辑视图上的数据时调用此函数创建编辑组件
    QWidget *createEditor(QWidget *parent,
                          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 updateEditorGeometry(QWidget *editor,
                              const QStyleOptionViewItem &option,
                              const QModelIndex &index) const;

    //编辑框绘制函数
    void paint(QPainter *painter,
               const QStyleOptionViewItem &option, const QModelIndex &index) const;

public slots:
    //编辑器组件关闭时的槽函数
    void onCloseEditor(QWidget* editor);

    //新数据提交时的槽函数
    void onCommitData(QWidget* editor);
};

#endif // MYQSTYLEDITEMDELEGATE_H

//MyQStyledItemDelegate.cpp
#include "MyQStyledItemDelegate.h"
#include <QDebug>
#include <QCheckBox>
#include <QComboBox>

MyQStyledItemDelegate::MyQStyledItemDelegate(QObject *parent) :
    QStyledItemDelegate(parent)
{
    connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(onCloseEditor(QWidget*)));
    //connect(this, SIGNAL(commitData(QWidget*)), this, SLOT(onCommitData(QWidget*)));
}


//用户编辑视图上的数据时调用此函数创建编辑组件
QWidget *MyQStyledItemDelegate::createEditor(QWidget *parent,
                      const QStyleOptionViewItem &option,
                      const QModelIndex &index) const
{
    QWidget* ret = NULL;

    m_index = index;

    //若是bool类型,改变交互编辑组件为复选框
    if (index.data().type() == QVariant::Bool)
    {
        QCheckBox* cb = new QCheckBox(parent);
        cb->setText("Check to True");
        ret = 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");

        ret = cb;
    }
    else //其他类型的数据项默认处理
        ret = QStyledItemDelegate::createEditor(parent, option, index);

    return ret;
}

//通过索引从模型中拿数据并设置到编辑框
void MyQStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    //将模型中的数据项转为bool类型设置到视图
    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
        QStyledItemDelegate::setEditorData(editor,index);
}

//将编辑框上的数据写到模型中
void MyQStyledItemDelegate::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().at(0), Qt::DisplayRole);
        }
    }
    else
        QStyledItemDelegate::setModelData(editor, model, index);
}


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

//更新编辑组件大小,用户在编辑数时可能原先大小的编辑框不够使用
void MyQStyledItemDelegate::updateEditorGeometry(QWidget *editor,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const
{
    QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}

//编辑器组件关闭时的槽函数
void MyQStyledItemDelegate::onCloseEditor(QWidget* editor)
{
    qDebug() << "slot: onCloseEditor";
    m_index = QModelIndex();    //清空m_index]
}

//新数据提交时的槽函数
void MyQStyledItemDelegate::onCommitData(QWidget* editor)
{
    qDebug() << "slot: onCommitData";
}

//Widget.h
class Widget : public QWidget
{
    Q_OBJECT
    QTableView          m_view;
    QStandardItemModel  m_model;
    QPushButton         m_btn;

    MyQStyledItemDelegate m_delegate;       //只增加这一行

    void initView();
    void initModel();

private slots:
    void onBtnClicked();

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

//Widget.cpp
void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(400, 100);

    m_view.setItemDelegate(&m_delegate);        //只增加这一行
}

  运行:

这里写图片描述

  • 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、付费专栏及课程。

余额充值