Как QLineEdit запускает QAbstractItemDelegate::commitData

Я разрабатываю приложение Qt, используя QTreeView и QAbstractItemModel. Модель содержит несколько разнородные данные, что требует различных элементов управления для редактирования. Я реализую его с помощью специального делегата, который запрашивает model.data(Qt::EditRole), и модель возвращает QWidget для использования в качестве редактора.

Мне нравится, как это работает для QLineEdit — когда я нажимаю Enter, автоматически вызывается delegate::setModelData, поэтому мне не нужно подключать QLineEdit::editingFinished к QAbstractItemDelegate::setModelData(), что очень удобно, поскольку QLineEdit возвращается из модели как QWidget делегату, и ему все равно, что это за виджет. С QComboBox это немного сложно - я хочу, чтобы comboBox зафиксировался после того, как выбор сделан, пока я могу сделать это только с помощью connect(myComboBox, SIGNAL(activated(QString)), myDelegate, commitData())); Однако мой делегат не знает тип виджета редактора, и я не хочу редактировать код делегата для каждого нового редактора, который модель передает ему. Мне бы очень хотелось, чтобы выпадающий список делал то же самое, что и QLineEdit, когда нажимается ввод в слоте, подключенном к его сигналу activated().

Итак, что же делает QLineEdit для вызова делегата setModelData? Есть ли в Qt общий способ для редактора сказать: «Я закончил редактирование, взял данные и передал их модели»?


person Serge    schedule 05.05.2015    source источник


Ответы (1)


QStyledItemDelegateQItemDelegate) определяет фильтр событий, который вызывает commitData, если вы нажимаете клавишу Tab или клавишу возврата.

См. следующий отрывок из Исходный код Qt 4.8.6:

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;
}
person titusjan    schedule 06.05.2015
comment
Спасибо, Titusjan, мне помогло следующее: void CommittingCombo::_activated(const QString&) { static_cast‹QWidget*›(parent())-›setFocus(); QEvent e(QEvent::FocusOut); QApplication::sendEvent(это, &e); } Интересно, однако, почему я должен был отправить событие о потере фокуса после смещения фокуса на родителя - я надеялся, что это произойдет автоматически... - person Serge; 06.05.2015