简介
在模型/视图结构中,代理的作用就是在视图组件进入编辑状态编辑某个项时,提供一个临时的编辑器用于数据编辑,编辑完成后再把数据提交给数据模型。例如,在 QTableView 组件上双击一个单元格时,代理会提供一个临时的编辑器,默认是 QLineEdit 编辑框,在这个编辑框里修改项 的文字,按 Enter 键或焦点移动到其他单元格时完成编辑,编辑框内的文字会保存到数据模型。
自定义代理类需要从 QStyledItemDelegate 类继承,创建自定义代理类的实 例后,再将其设置为整个视图组件或视图组件的某行或某列的代理,以替代默认代理的功能。
QAbstractItemView 类定义了设置自定义代理的 3 个函数,分别是整个视图组件的代理、为某一列设置自定义代理、为某一行设置自定义代理,函数定义如下:
void setItemDelegate(QAbstractItemDelegate *delegate)
void setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
void setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)
QStyledItemDelegate 是视图组件使用的默认代理类,一般使用 QStyledItemDelegate 作为自定
义代理类的父类。要自定义一个代理类,必须重新实现 QStyledItemDelegate 中定义的 4 个虚函数。 这 4 个函数是由模型/视图系统自动调用的。
1.函数 createEditor()
函数 createEditor()可创建用于编辑模型数据的界面组件,称为代理编辑器,例如 QSpinBox
组件,或 QComboBox 组件。其函数原型定义如下:
QWidget *QStyledItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index)
其中,parent 是要创建的组件的父组件,一般就是窗口对象;option 是项的一些显示选项,是
QStyleOptionViewItem 类型的,包含字体、对齐方式、背景色等属性;index 是项在数据模型中的
模型索引,通过 index->model()可以获取项所属数据模型的对象指针。
2.函数 setEditorData()
函数 setEditorData()的功能是从数据模型获取项的某个角色(一般是 EditRole 角色)的数据, 然后将其设置为代理编辑器上显示的数据。其函数原型定义如下:
void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
参数 editor 就是前面用函数 createEditor()创建的代理编辑器,通过 index->model()可以获取项所属数据模型的对象指针,从而获取项的数据,然后将其显示在代理编辑器上。
3.函数 setModelData()
完成对当前单元格的编辑,例如输入焦点移到其他单元格时,系统会自动调用函数 setModelData(),
其功能是将代理编辑器里的输入数据保存到数据模型的项里。其函数原型定义如下:
void QStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index)
其中,editor 是代理编辑器,model 是数据模型,index 是所编辑的项在模型中的模型索引。
4.函数 updateEditorGeometry()
视图组件在界面上显示代理编辑器时,需要调用 updateEditorGeometry()函数为组件设置合适 的大小,例如在一个单元格里显示一个 QSpinBox 代理编辑器时,一般将其设置为单元格的大小。
其函数原型定义如下:
void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index)
其中,变量 option->rect 是 QRect 类型,表示代理编辑器的建议大小。一般将代理编辑器大小设 置为建议大小即可,即用下面的一行代码:
editor->setGeometry(option.rect);
案例:自定义代理类TFloatSpinDelegate
在QT Creator中新建C++类,并设置基类为 QStyledItemDelegate,要勾选 Add Q_OBJECT 复选框,因为要在 TFloatSpinDelegate 类中插入 Q_OBJECT 宏。
然后实现基类中的虚函数即可。
cpp
QWidget *TFloatSpinDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(20000);
editor->setDecimals(2); //显示两位小数
return editor;
}
void TFloatSpinDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
float value = index.model()->data(index, Qt::EditRole).toFloat();
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
spinBox->setValue(value);
}
void TFloatSpinDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model, const QModelIndex &index) const
{
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
// spinBox->interpretText();
float value = spinBox->value();
QString str=QString::asprintf("%.2f",value);
model->setData(index, str, Qt::EditRole); //保存到数据模型
}
void TFloatSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
案例:自定义代理类TComboBoxDelegate
同样创建一个新的C++类,并实现基类的4个虚函数
cpp
void TComboBoxDelegate::setItems(QStringList items, bool editable)
{
m_itemList=items;
m_editable=editable;
}
QWidget *TComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QComboBox *editor = new QComboBox(parent); //自定义的组件
editor->setEditable(m_editable); //是否可编辑
for (int i=0;i<m_itemList.count();i++) //从字符串列表初始下拉列表
editor->addItem(m_itemList.at(i));
return editor;
}
void TComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString str = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentText(str);
}
void TComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString str = comboBox->currentText();
model->setData(index, str, Qt::EditRole);
}
void TComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
自定义函数 setItems()用于设置变量 m_itemList 和 m_editable 的值,在函数 createEditor()里动态
创建 QComboBox 组件时,用于设置下拉列表的内容以及是否可以编辑。
使用自定义代理
cpp
m_model = new QStandardItemModel(2,FixedColumnCount,this); //创建数据模型
m_selection = new QItemSelectionModel(m_model, this); //创建选择模型
//为tableView设置数据模型
ui->tableView->setModel(m_model); //设置数据模型
ui->tableView->setSelectionModel(m_selection); //设置选择模型
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
///注意设置的这些代理组件只有在编辑时才有效果
intSpinDelegate= new TSpinBoxDelegate(this); ///setItemDelegate是整个视图组件的代理
ui->tableView->setItemDelegateForColumn(0, intSpinDelegate); //测深 setItemDelegateForColumn为某一列的代理
floatSpinDelegate = new TFloatSpinDelegate(this);
ui->tableView->setItemDelegateForColumn(1, floatSpinDelegate); //垂深
ui->tableView->setItemDelegateForColumn(2, floatSpinDelegate); //方位
ui->tableView->setItemDelegateForColumn(3, floatSpinDelegate); //总位移
comboDelegate = new TComboBoxDelegate(this);
QStringList strList;
strList<<"优"<<"良"<<"一般"<<"不合格";
comboDelegate->setItems(strList,false);
ui->tableView->setItemDelegateForColumn(4, comboDelegate); //固井质量
这样增加了自定义代理功能后,在编辑"测深"列时,会在单元格的位置出现一个 SpinBox 用于输入整数;在编辑第二到四列的浮点数时,会出现一个 DoubleSpinBox 用于输入浮点数;在 编辑"固井质量"列时,会出现一个下拉列表框用于从下拉列表中选择一个字符串。