与MVC架构(模型-视图-控制器模式)不同,模型/视图结构中不包括一个完全独立的组件来管理与用户的交互。通常,视图负责将模型数据呈现给用户,并处理用户输入。为了在获取输入的方式上提供一些灵活性,交互由委托完成。这些组件提供输入功能,并负责渲染某些视图中的单个项。控制委托的标准接口在QAbstractItemDelegate类中定义。
委托应能够通过实现paint()和sizeHint()函数自行渲染其内容。但是,简单基于小部件的委托可以将QItemDelegate作为QAbstractItemDelegate的子类,并利用这些函数的默认实现。
委托的编辑器可以通过使用小部件来管理编辑过程,或直接处理事件来实现。
Qt提供的标准视图使用QItemDelegate的实例提供编辑功能。默认委托接口的实现为标准视图(QListView、QTableView和QTreeView)中的每个项呈现常规样式。
默认委托处理了所有标准角色。
视图使用的委托由itemDelegate()函数返回。setItemDelegate()函数允许为标准视图安装自定义委托,在设置自定义视图的委托时需要使用此函数。
接下来,使用自定义委托QSpinBox来提供编辑工具,主要用于显示整数的模型。
先设置一个自定义的基于整数的表模型QStandardItemModel
,因为自定义委托控制数据输入。之后创建一个表视图来显示模型的内容,使用自定义委托进行编辑。
可以继承QStyledItemDelegate
来实现自定义委托。
创建自定义委托需要实现
// 创建自定义Widget
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE;
// 设置数据到控件
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
// 设置数据到模型
void setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const Q_DECL_OVERRIDE;
// 更新控件尺寸
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE;
具体实现:
QWidget *C_SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
void C_SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void C_SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void C_SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
createEditor()
:当视图需要一个编辑器时,它会告诉委托为被修改的项提供一个编辑器控件。该函数提供了该功能,在这个函数,不需要为控件保留一个指针,因为视图会在不再需要该控件的时候进行删除。
setEditorData()
:委托将模型中的数据设置到控件中,控件需要判断类型,转换为合适的类型。
setModelData()
:编辑完控件后,调用此函数告诉委托将编辑好的数据设置到模型中。正常情况下,该函数完成后要发送closeEidt()
信号告诉视图,视图对控件进行关闭和销毁。
updateEditorGeometry()
:告知委托设置控件的几何布局。
调用:
QStandardItemModel *model = new QStandardItemModel(8, 4, this);
QTableView *table = new QTableView(0);
table->setModel(model);
table->setItemDelegateForColumn(0, new C_SpinBoxDelegate);
setCentralWidget(table);
编辑完成后,委托应该向其他组件提供关于编辑过程结果的提示,提供提示也有助于后续编辑操作。这个可以通过发出closeEditor()信号时使用合适的提示来实现。它们会被构造编辑控件时安装的默认QItemDelegate事件过滤器来捕获和处理的。
可以通过调整编辑控件的行为来使交互更加友好。在QItemDelegate提供的默认事件过滤器中,如果用户在编辑中按下回车键,委托会将值提交给模型并关闭控件。可以通过在控件上安装自己的事件过滤器来更改这种行为,并提供适合需求的编辑提示;例如,可以在发射closeEditor()信号时使用EditNextItem提示来实现编辑视图中的下一个项目。
另一种不需要使用事件过滤器的方法是提供自己的编辑控件,比如子类化QSpinBox。这种替代方法会对编辑控件有更多的控制,但需要编写额外的代码。如果需要自定义标准Qt编辑控件的行为,通常在委托中中安装事件过滤器更加便捷。
委托操作后可以不提供提示,但不提供提示的委托与应用程序集成程度较低,而且不如发出提示来支持常见编辑操作的委托好用。
委托是通过继承QStyledItemDelegate
类,实现其相关方法,进而改变视图的默认操作。
咱们假设一下,可能项目需要,进行密码密文编辑,下拉框委托(QComboBox),颜色选择委托,图标委托等。那么每实现一个委托,就要单独继承一次QStyledItemDelegate类吗?当然没问题,但会不会但累赘了?
那有没有这样一种可能,创建一个类,并继承QStyledItemDelegate,然后将常用的委托全部实现,然后创建一些接口,通过这些接口对编辑器进行选择和初始化。
答案是肯定的。
思想 > 盲目操作。