I am developing a Qt application using QTreeView
and QAbstractItemModel
.
The model contains somewhat heterogenous data, which requires different controls for editing. I am implementing it by having a custom delegate, that queries model.data(Qt::EditRole)
and the model would return the QWidget
to be used as an editor.
I like how it works for QLineEdit
- when I hit enter, delegate::setModelData
is called automatically, so I do not have to connect QLineEdit::editingFinished
to QAbstractItemDelegate::setModelData()
, which is very convenient since the QLineEdit is returned from the model as QWidget
to the delegate and it doesn't have to care what kind of widget it is.
With QComboBox
it's a bit tricky - I want the comboBox to commit once the selection is made, so far I can only do it by connect(myComboBox, SIGNAL(activated(QString)), myDelegate, commitData()));
However, my delegate doesn't know the type of the editor widget and I do not want to edit the delegate's code for each new editor the model ends up passing it. I would really like to force combobox to do the same thing as QLineEdit does when the enter is clicked in the slot connected to its activated()
signal.
So, what does the QLineEdit
do to call delegate's setModelData
?
Is there a generic way in Qt for the editor whatever it is to say "I'm done editing, take the data and pass it to the model"?
The QStyledItemDelegate
(and QItemDelegate
) defines an event filter that calls commitData
if you press tab or return.
See the following excerpt from the Qt 4.8.6 source:
bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
{
QWidget *editor = qobject_cast<QWidget*>(object);
if (!editor)
return false;
if (event->type() == QEvent::KeyPress) {
switch (static_cast<QKeyEvent *>(event)->key()) {
case Qt::Key_Tab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
return true;
case Qt::Key_Backtab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
return true;
case Qt::Key_Enter:
case Qt::Key_Return:
#ifndef QT_NO_TEXTEDIT
if (qobject_cast<QTextEdit *>(editor) || qobject_cast<QPlainTextEdit *>(editor))
return false; // don't filter enter key events for QTextEdit
// We want the editor to be able to process the key press
// before committing the data (e.g. so it can do
// validation/fixup of the input).
#endif // QT_NO_TEXTEDIT
#ifndef QT_NO_LINEEDIT
if (QLineEdit *e = qobject_cast<QLineEdit*>(editor))
if (!e->hasAcceptableInput())
return false;
#endif // QT_NO_LINEEDIT
QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
Qt::QueuedConnection, Q_ARG(QWidget*, editor));
return false;
case Qt::Key_Escape:
// don't commit data
emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
break;
default:
return false;
}
if (editor->parentWidget())
editor->parentWidget()->setFocus();
return true;
} else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
//the Hide event will take care of he editors that are in fact complete dialogs
if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
QWidget *w = QApplication::focusWidget();
while (w) { // don't worry about focus changes internally in the editor
if (w == editor)
return false;
w = w->parentWidget();
}
#ifndef QT_NO_DRAGANDDROP
// The window may lose focus during an drag operation.
// i.e when dragging involves the taskbar on Windows.
if (QDragManager::self() && QDragManager::self()->object != 0)
return false;
#endif
emit commitData(editor);
emit closeEditor(editor, NoHint);
}
} else if (event->type() == QEvent::ShortcutOverride) {
if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
event->accept();
return true;
}
}
return false;
}