1.模型视图中的委托
- 委托(Delegate)是视图中处理用户输入的部件
- 视图可以设置委托对象用于处理用户输入
- 委托对象负责创建和显示用户输入上下文(编辑框的创建和显示)
2.QAbstractItemDelegate用于定义委托的功能接口
QItemDelegates标准委托功能的实现
3.委托中的编辑器
- 委托能够提供编辑时需要的上下文环境
- 不同委托提供的编辑器类型不同(文本框、单选框等)
- 编辑器用从模型获取数据,并将编辑结果返回模型
4.委托中的关键函数
- createEditor需要编辑数据时,创建编辑器组件
- updateEditorGeometry更新编辑器组件的大小
- setEditorData通过索引从模型中获取数据
- setModelData将编辑后的新数据返回模型
5.委托中的关键信号
- void closeEditor(QWidget* editor) 编辑器组件关闭信号
- void commitData(QWidget* editor) 新数据提交信号
Widget.h
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include "SubStyledItemDelegate.h"
class Widget : public QWidget
{
Q_OBJECT
QTableView m_view;
QStandardItemModel m_model;
QPushButton m_testBtn;
SubStyledItemDelegate m_delegate;
void initView();
void initModel();
private slots:
void onTestBtnClicked();
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
initView();
initModel();
m_view.setModel(&m_model);
m_testBtn.setParent(this);
m_testBtn.move(10, 120);
m_testBtn.resize(300, 30);
m_testBtn.setText("Test");
connect(&m_testBtn, SIGNAL(clicked()), this, SLOT(onTestBtnClicked()));
}
void Widget::initView()
{
m_view.setParent(this);
m_view.move(10, 10);
m_view.resize(300, 100);
m_view.setItemDelegate(&m_delegate);
}
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::onTestBtnClicked()
{
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(Qt::DisplayRole).toString();
qDebug() << text;
}
qDebug() << endl;
}
qDebug() << "Current View Delegate: " << m_view.itemDelegate();
}
Widget::~Widget() = default;
SubStyledItemDelegate.h
#ifndef SUBSTYLEDITEMDELEGATE_H
#define SUBSTYLEDITEMDELEGATE_H
#include <QObject>
#include <QStyledItemDelegate>
class SubStyledItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
private slots:
void onCloseEditor(QWidget* editor);
void onCommitData(QWidget* editor);
public:
explicit SubStyledItemDelegate(QObject* parent = 0);
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;
};
#endif // SUBSTYLEDITEMDELEGATE_H
SubStyledItemDelegate.cpp
#include "SubStyledItemDelegate.h"
#include <QDebug>
SubStyledItemDelegate::SubStyledItemDelegate(QObject* parent) :
QStyledItemDelegate(parent)
{
connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(onCloseEditor(QWidget*)));
connect(this, SIGNAL(commitData(QWidget*)), this, SLOT(onCommitData(QWidget*)));
}
QWidget* SubStyledItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
qDebug() << "SubStyledItemDelegate::createEditor";
return QStyledItemDelegate::createEditor(parent, option, index);
}
void SubStyledItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
qDebug() << "SubStyledItemDelegate::updateEditorGeometry";
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}
void SubStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
qDebug() << "SubStyledItemDelegate::setEditorData";
QStyledItemDelegate::setEditorData(editor, index);
}
void SubStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
qDebug() << "SubStyledItemDelegate::setModelData";
QStyledItemDelegate::setModelData(editor, model, index);
}
void SubStyledItemDelegate::onCloseEditor(QWidget* editor)
{
qDebug() << "SubStyledItemDelegate::onCloseEditor";
}
void SubStyledItemDelegate::onCommitData(QWidget* editor)
{
qDebug() << "SubStyledItemDelegate::onCommitData";
}
运行结果:当编辑框中内容被更改后:
SubStyledItemDelegate::createEditor
SubStyledItemDelegate::updateEditorGeometry
SubStyledItemDelegate::setEditorData
SubStyledItemDelegate::onCommitData
SubStyledItemDelegate::setModelData
SubStyledItemDelegate::setEditorData
SubStyledItemDelegate::onCloseEditor
当Test按钮被点击时
Model Data
Row: 0
"A"
"B"
Row: 1
"C"
"D"
Current View Delegate: QStyledItemDelegate(0x2da60c0)
6.委托的本质
- 为视图提供数据编辑的上下文环境
- 产生界面元素的工厂类
- 能够使用和设置模型中的数据
7.自定义委托时需要重写函数
- createEditor需要编辑数据时,创建编辑器组件
- updateEditorGeometry更新编辑器组件的大小和位置
- setEditorData通过索引从模型中获取数据
- setModelData将编辑后的新数据返回模型
CustomizedItemModel.h
#ifndef CUSTOMIZEDITEMMODEL_H
#define CUSTOMIZEDITEMMODEL_H
#include <QItemDelegate>
#include <QObject>
#include <QModelIndex>
class CustomizedItemModel : public QItemDelegate
{
Q_OBJECT
protected slots:
//void onCloseEditor(QWidget*);
public:
CustomizedItemModel(QObject* parent = 0);
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;
};
#endif // CUSTOMIZEDITEMMODEL_H
CustomizedItemModel.cpp
#include "CustomizedItemModel.h"
#include <QCheckBox>
#include <QComboBox>
#include <QApplication>
CustomizedItemModel::CustomizedItemModel(QObject* parent) :
QItemDelegate(parent)
{
//connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(onCloseEditor(QWidget*)));
}
QWidget* CustomizedItemModel::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* ret = NULL; //用于存储要返回的编辑器控件
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 = QItemDelegate::createEditor(parent, option, index);
}
return ret;
}
void CustomizedItemModel::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect); //包含了当前单元格在视图中的坐标和尺寸
}
void CustomizedItemModel::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 CustomizedItemModel::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 CustomizedItemModel::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// if (index.data().type() == QVariant::Bool)
// {
// bool value = index.data().toBool();
// QStyleOptionButton checkBoxOption;
// checkBoxOption.rect = option.rect; // 使用单元格区域
// checkBoxOption.state = QStyle::State_Enabled;
// checkBoxOption.state |= value ? QStyle::State_On : QStyle::State_Off;
// QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkBoxOption, painter);
// }
// else
// {
// QItemDelegate::paint(painter, option, index);
// }
QItemDelegate::paint(painter, option, index);
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include "CustomizedItemModel.h"
class Widget : public QWidget
{
Q_OBJECT
QTableView m_view;
QStandardItemModel m_model;
CustomizedItemModel m_delegate;
void initView();
void initModel();
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include <QStringList>
#include <QChar>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
initView();
initModel();
m_view.setModel(&m_model);
for(int i=0; i<m_model.columnCount(); i++)
{
m_view.setColumnWidth(i, 125);
}
}
void Widget::initView()
{
m_view.setParent(this);
m_view.move(10, 10);
m_view.resize(500, 300);
m_view.setItemDelegate(&m_delegate);
}
void Widget::initModel()
{
QStandardItem* root = m_model.invisibleRootItem();
QStringList hl;
QStandardItem* itemA1 = new QStandardItem();
QStandardItem* itemB1 = new QStandardItem();
QStandardItem* itemC1 = new QStandardItem();
QStandardItem* itemA2 = new QStandardItem();
QStandardItem* itemB2 = new QStandardItem();
QStandardItem* itemC2 = new QStandardItem();
hl.append("Language");
hl.append("Level");
hl.append("script");
m_model.setHorizontalHeaderLabels(hl);
itemA1->setData("nnn", Qt::DisplayRole);
itemB1->setData(QChar('A'), Qt::DisplayRole);
itemC1->setData(false, Qt::DisplayRole);
itemA2->setData("hhh", Qt::DisplayRole);
itemB2->setData(QChar('B'), Qt::DisplayRole);
itemC2->setData(true, Qt::DisplayRole);
root->setChild(0, 0, itemA1);
root->setChild(0, 1, itemB1);
root->setChild(0, 2, itemC1);
root->setChild(1, 0, itemA2);
root->setChild(1, 1, itemB2);
root->setChild(1, 2, itemC2);
}
Widget::~Widget() = default;