createEditor(): 返回修改数据的组件;
setEditorData(): 为editor提供编辑的原始数据;
updateEditorGeometry(): 保证editor显示在 item view 的合适位置以及大小;
setModelData(): 根据editor 的数据更新model的数据。
下面我们使用一个音轨编辑器的例子来说明这些函数的用法,实现一个自定义的委托,这个例子来自于:(C++ GUI Programming with Qt4, 2nd Edition)
首先我们自定义了一个数据类型,用于存储音轨的标题(QString)和持续时间(int):
class Track
{
public:
Track(const QString &title = "", int duration = 0);
QString title;
int duration;
};
关于这个类的实现这里就不提了,接下来我们创建整个界面的布局,这里也只给出构造函数:
TrackEditor::TrackEditor(QList
其中tableWidget->setItemDelegate(new TrackDelegate(1));告诉编译器我们使用TrackDelegate这个对象作为我们的委托,这里
TrackDelegate就是我们自定义的委托。效果图如下:
我们可以通过双击来对音轨的持续时间进行编辑,下面我们着重讨论这个自定义委托类的实现过程,像上面提到的那样,我们得重新实现 void paint()、QWidget *createEditor()、void setEditorData()、void setModelData()四个函数。下面是这个类的定义部分
#ifndef TRACKDELEGATE_H
#define TRACKDELEGATE_H
#include
class TrackDelegate : public QItemDelegate
{
Q_OBJECT
public:
TrackDelegate(int durationColumn, QObject *parent = 0);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
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;
private slots:
void commitAndCloseEditor();
private:
int durationColumn;
};
#endif
我们用 durationColumn来存储我们需要设置委托的列。
TrackDelegate::TrackDelegate(int durationColumn, QObject *parent)
: QItemDelegate(parent)
{
this->durationColumn = durationColumn;
}
构造函数接收一个列数告诉这个委托,那一列需要被编辑。
void TrackDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == durationColumn) {
int secs = index.model()->data(index, Qt::DisplayRole).toInt();
QString text = QString("%1:%2")
.arg(secs / 60, 2, 10, QChar('0'))
.arg(secs % 60, 2, 10, QChar('0'));
QStyleOptionViewItem myOption = option;
myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
drawDisplay(painter, myOption, myOption.rect, text);
drawFocus(painter, myOption, myOption.rect);
} else{
QItemDelegate::paint(painter, option, index);
}
}
paint()函数用于控制某一列的显示格式,因为用户传进去的值是秒数,所以我们得重绘这个显示,用“分钟:秒”、右对齐的格式显示这些数据。
QWidget *TrackDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == durationColumn) {
QTimeEdit *timeEdit = new QTimeEdit(parent);
timeEdit->setDisplayFormat("mm:ss");
connect(timeEdit, SIGNAL(editingFinished()),
this, SLOT(commitAndCloseEditor()));
return timeEdit;
} else {
return QItemDelegate::createEditor(parent, option, index);
}
}
当我们双击一个单元格时 createEditor()函数被调用并在当前单元格创建一个QTimeEdit用于编辑,但要注意我们双击出来的数据并不在这会儿就写入单元格,即当我们把焦点聚焦在另一个单元格时,我们在这个时间编辑器编辑的时间会失效,单元格显示我们原来写入的时间,所以我们连接一个槽,当编辑完后焦点转移就会触发这个槽,这个槽是这样实现的:
void TrackDelegate::commitAndCloseEditor()
{
QTimeEdit *editor = qobject_cast(sender());
emit commitData(editor);
emit closeEditor(editor);
}
这个槽发射commitData()信号通知视图用被编辑的数据替换已经存在的数据,并发射closeEditor()信号通知视图已经不再需要这个编辑器了,这时模型就会删除它。
void TrackDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
if (index.column() == durationColumn) {
int secs = index.model()->data(index, Qt::DisplayRole).toInt();
QTimeEdit *timeEdit = qobject_cast(editor);
timeEdit->setTime(QTime(0, secs / 60, secs % 60));
} else {
QItemDelegate::setEditorData(editor, index);
}
}
当用户初始化编辑的时候是调用createEditor()创建一个编辑器的,这我们上边也提到过, 然后这个项的当前数据调用setEditorData()这个函数来初始化编辑器(原来是拿00:00来初始化的)。
void TrackDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
if (index.column() == durationColumn) {
QTimeEdit *timeEdit = qobject_cast(editor);
QTime time = timeEdit->time();
int secs = (time.minute() * 60) + time.second();
model->setData(index, secs);
} else {
QItemDelegate::setModelData(editor, model, index);
}
}
如果用户完成了编辑,就从QTimeEdit中提取出分钟数和秒数,并且设置数据为相应的秒数,paint函数就会把这个秒数以当前的格式写进对应的单元格中。
至此我们简单扼学完了Qt的MVC框架,但这只是入门之说,仍需努力,努力,再努力。