当委托失去焦点时,取消在 QTableView 中的编辑

Cancel edit in QTableView when delegate loses focus

本文关键字:QTableView 编辑 取消 失去 焦点      更新时间:2023-10-16

我正在使用基本的QTableView + QAbstractTableModel架构。数据可以通过 QAbstractItemDelegate 子类进行编辑。一切都按预期工作,但我需要稍微改变编辑行为:

Qt 默认行为为:

  • 对于打开的委托编辑器,按 Enter 确认编辑
  • 对于打开的委托编辑器,点击 Esc 会取消编辑
  • 对于打开的委托编辑器,单击其他表视图单元格可确认编辑

我想摆脱最后一个。如果委托编辑器失去焦点,则应取消编辑(即,它不应发出 editorDone()),以便用户只能通过按 Enter 来提交更改。

有没有方便的方法可以做到这一点?

提前致谢

我在应用程序中遇到了同样的问题,这是我的解决方案:

深入研究QAbstractItemDelegate的源代码(通过查找commitData),您可以找到一个私有函数editorEventFilter(QObject *object, QEvent *event),它具有以下代码(简化):

if (event->type() == QEvent::KeyPress) {
    // just handling Esc/Enter/Return key strokes...
} else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
    // ok it's now handling focus related events
    if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
        QWidget *w = QApplication::focusWidget();
        // lines omitted: checking focus is in editor or editor's child
        if (tryFixup(editor))
            emit q->commitData(editor);  // <- here
    }
} // else if (...) {...}

这是tryFixup函数:

bool QAbstractItemDelegatePrivate::tryFixup(QWidget *editor)
{
    if (QLineEdit *e = qobject_cast<QLineEdit*>(editor)) {
        if (!e->hasAcceptableInput()) {
            // fix input using validator.fixup()
            // if QLineEdit has a QValidator
            return e->hasAcceptableInput();
        }
    }
    return true;
}

因此,在大多数情况下,QAbstractItemDelegate 会在编辑器失去焦点时提交编辑器的数据。为了防止这种行为,我们可以覆盖eventFilter并过滤掉QFocusOutEventQHideEvent。我正在使用 PySide6 6.5.0,这是代码:

def checkObjectIsEditor(self, obj):
    return False  # maybe something like isinstance(obj, YourEditorType)
def eventFilter(self, object, event):
    if (
        self.checkObjectIsEditor(object)
        and event.type() in [QEvent.Type.FocusOut, QEvent.Type.Hide]
    ):
        widget = QApplication.focusWidget()
        while widget:
            if self.checkIsEditor(widget):
                return False
            widget = widget.parentWidget()
        # The focus is neither in editor nor in its child.
        # Now close the editor, and not commiting its data.
        object.hide()
        object.deleteLater()
        # or maybe self.destroyEditor(object)
        return True  # stop the event from propagating
    return super().eventFilter(object, event)

希望这能帮助任何有需要的人。

覆盖类,找出最后一个行为是你不想要的函数,然后覆盖该函数并将其实现留空,然后像往常一样调用它,但现在它什么都不做(或者做其他事情,你正在编程让它做任何你想做的事情)

仅在用户与编辑器小部件交互时在编辑器小部件上设置属性,并在修改模型之前检查此属性是否正确设置setModelData()

例如,我们有一个 ButtonDelegate,它似乎在聚焦时被触发。我们应用了一个属性来了解setModelData()何时由点击触发,如下所示:

QWidget * ButtonDelegate::createEditor(QWidget * parent,
                                       QStyleOptionViewItem const &,
                                       QModelIndex const &) const
{
    auto editor = new QToolButton(parent);
    connect(editor, &QAbstractButton::clicked, this, [=]
    {
        editor->setProperty("clicked", true);
        Q_EMIT const_cast<ButtonDelegate *>(this)->commitData(editor);
    });
    return editor;
}
void ButtonDelegate::setModelData(QWidget * editor, QAbstractItemModel * model,
                                  QModelIndex const & index) const
{
    if (model && index.isValid() && editor->property("clicked") == true)
    {
        model->setData(index, true, Qt::CheckStateRole);
        editor->setProperty("clicked", {}); // Reset to handle next click
    }
}

我会覆盖模型中的QAbstractItemModel::setData(),并在尝试设置与给定项目已经存在的相同数据时跳过发出dataChanged()