Problem with pressing TAB key during QTableView cell edit
-
Hello all,
PROBLEM
I have a problem with the editing behaviour in a QTableView (i'm using Qt4.8). I have a QTableView in which i defined custom ItemDelegates with custom editors :
- a custom widget A which inherits from QComboBox;
- a custom widget B which inherits from QWidget and that contains a custom QDoubleSpinBox which inherits from QDoubleSpinBox;
When i start edit a cell, a TAB keypress validates the current edition an the next cell becomes editable, which is the standard behaviour of QTableView. But with my custom editor widget B, when i press TAB during the edition, the focus is given to the next cell, but not in an editing mode. I don't have this problem with my custom widget A. I guess it is because this widget is composed with an internal spinbox.
RESEARCH
When a view calls createEditor function of delegate it also installs the delegate event filter to editor, so i guess that events on editor widgets are trapped and treated by the ItemDelegate :
QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, const QStyleOptionViewItem &options) { Q_Q(QAbstractItemView); QWidget *w = editorForIndex(index).widget.data(); if (!w) { QAbstractItemDelegate *delegate = delegateForIndex(index); if (!delegate) return 0; w = delegate->createEditor(viewport, options, index); if (w) { w->installEventFilter(delegate); ...... }
With the widget A, which work perfectly, the keyPressEvent is treated in eventFilter method of my delegate :
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); }
With my widget B, this eventFilter method doesn't receive the KeyPressEvent which is actually treated by the QWidget::event(QEvent *event) method of my internal spinbox, which explains why it does a focusNextPrevChild instead of the closeEditor(editor, QAbstractItemDelegate::EditNextItem) that i would expect.
case QEvent::KeyPress: { QKeyEvent *k = (QKeyEvent *)event; bool res = false; if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? if (k->key() == Qt::Key_Backtab || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) res = focusNextPrevChild(false); else if (k->key() == Qt::Key_Tab) res = focusNextPrevChild(true); if (res) break; }
WHY ?
I don't undestand how to resolve my problem, and why this keyPressEvent on my custom editor B is not treated by the delegate.
In my custom editor widget B, i already did this :
setFocusPolicy(Qt::WheelFocus); setFocusProxy(internalSpinbox);
Anyone to save me ? :)
-
@Ben35 said:
I don't undestand how to resolve my problem, and why this keyPressEvent on my custom editor B is not treated by the delegate.
Because the item delegate is only installed as event filter on the widget you create as editor. But you need to filter events of a child widget.
So when creating the editor widget you also need to make sure to install the item delegate (this) as event filter on the sub-editor-widget. -
I think i've found a solution.
First, i install an eventFilter in my custom editor widget B on the internal spinbox :
internalSpinbox->installEventFilter(this);
Then, i implement eventFilter to treat the events on the spinbox and duplicate them to the parent:
bool AbstractRatioQuantitySpinbox::eventFilter(QObject *object, QEvent *event) { // cf http://stackoverflow.com/questions/12145522/why-pressing-of-tab-key-emits-only-qeventshortcutoverride-event if (event->type() == QEvent::KeyPress) { auto keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) { QApplication::postEvent( this, new QKeyEvent(keyEvent->type(), keyEvent->key(), keyEvent->modifiers())); return true; } } else if (event->type() == QEvent::FocusOut) { auto focusEvent = static_cast<QFocusEvent *>(event); QApplication::postEvent(this, new QFocusEvent(focusEvent->type(), focusEvent->reason())); return false; } return QWidget::eventFilter(object, event); }