新创建的表格是可以直接进行编辑,默认效果类似于QLineEdit,但是如果想要在表格上嵌入一些复杂的控件默认的效果就不行了,例如下面的例子:
其中在双击表格控件后会出现一个下拉框,这种效果就需要使用代理来实现,代码如下所示:
class VariableDelegate
: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit VariableDelegate(QObject* _parent = nullptr);
~VariableDelegate();
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
QWidget* VariableDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QComboBox* delegate_item = new QComboBox(parent);
delegate_item->addItems(QStringList() << "c_1" << "c_2");
return delegate_item;
}
// tableview给第二列设置代理控件
setItemDelegateForColumn(1, new VariableDelegate(this));
// tableview给全部单元格设置代理控件
setItemDelegate(new VariableDelegate(this));
// tableview给第二行设置代理控件
setItemDelegateForRow(1, new VariableDelegate(this));
要开发代理控件首先要实现QStyledItemDelegate
中的createEditor()
函数,将要放置的控件在该函数中创建并返回控件,在创建表格后将代理设置给QTableView
,就能实现代理效果。
在实现createEditor()
函数时必须将第一个参数(parent)
作为父类设置
给创建后需要返回的控件,否则控件不会出现在指定位置上。
最简单的代理控件的实现方式就是实现createEditor()函数即可,但是一般在开发代理控件时肯定有比较复杂的情况,因此面对复杂的情况代理的开发一般至少实现三个函数,具体如下表所示:
函数名称 | 描述 |
---|---|
createEditor() | 返回一个控件对象 |
setEditorData() | 将界面上显示的值设置到具体控件上 |
setModelData() | 返回给模型用户修改过的数据 |
创建一个复杂一点控件来分别解释一下这三个关键函数的作用,例如,可以将一个组合的自定义控件嵌入到表格中,组合自定义控件如下:
#include
class VariableItem
: public QWidget
{
public:
explicit VariableItem(QWidget* _parent = nullptr);
~VariableItem() override;
void SetText(const QString & _text);
QString GetText();
private:
void InitializeUI();
private:
QLineEdit* variable_show_;
QPushButton* variable_select_;
};
#include "variable_item.h"
VariableItem::VariableItem(QWidget *_parent)
: QWidget(_parent)
, variable_show_(nullptr)
, variable_select_(nullptr) {
InitializeUI();
}
VariableItem::~VariableItem() = default
void VariableItem::SetText(const QString & _text) {
variable_show_->setText(_text);
}
QString VariableItem::GetText() {
return variable_show_->text();
}
void VariableItem::InitializeUI() {
QHBoxLayout* variable_main_layout = new QHBoxLayout();
variable_main_layout->setMargin(0);
variable_show_ = new QLineEdit(this);
variable_main_layout->addWidget(variable_show_, 1);
QPushButton* variable_btn = new QPushButton("Select", this);
variable_main_layout->addWidget(variable_btn);
setLayout(variable_main_layout);
setContentsMargins(0, 0, 0, 0);
}
这段代码在普通的情况下直接可以独立显示出来,其中在窗体的水平方向上放置了一个QLineEdit
和QPushButton
,具体如下所示:
这种复杂的自定义控件同样可以放到createEditor()
函数中,作为一个代理控件返回,具体代码如下:
QWidget* VariableDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
VariableItem* item = new VariableItem(parent);
return item;
}
双击表格控件时就可以在单元格内看到该复杂控件,效果如下:
可以,这个控件是可以修改数据,但是当修改数据并离开单元格后,单元格没有显示当前输入的数据,这是就用到了setModelData()
函数,该函数可以将控件的数据设置给单元格去显示。
void VariableDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
VariableItem* item = qobject_cast(editor);
if (item != nullptr) {
model->setData(index, item->GetText(), Qt::EditRole);
}
}
注意设置Data的标识字段时
要设置Qt::EditRole
,该标志就是QTableView从Model取值显示时的标志,显示效果如下:
现在当离开单元格的编辑状态后已经可以在单元格中显示刚刚输入的内容了,可以配合按钮实现例如选择文件夹、文件或者日期等操作。
当再次双击单元格时,将显示的内容写入到复杂控件中可以重写setEditorData()
函数,该函数就可以从当前选中的单元格中返回内容,然后使用Qt::EditRole
标识值将数据取出设置即可。
void VariableDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
VariableItem* item = qobject_cast(editor);
if (item != nullptr) {
item->SetText(value);
}
}
至此基本表格代理控件就开发完成了。
在createEditor()
函数中会大量创建QWidget对象,这些对象会在setModelData()
结束后自动被析构因此不需要自己取手动进行管理。演示如下:
// 给代理控件添加析构函数,并打印数据
VariableItem::~VariableItem() {
qInfo() << "delete VariableItem";
}
// 添加数据打印
void VariableDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
qInfo() << "setModelData";
}