You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QItemDelegate:setModelData触发时机异常,如何改为对话框关闭时触发?

解决Qt委托中setModelData提前触发的问题

你的问题核心是QPushButton作为编辑器时,点击按钮会触发委托的默认提交逻辑,导致setModelData提前执行,而非等待对话框关闭。以下是具体的修复方案:

关键修改点

1. 避免重复打开持久编辑器

原代码中paint方法每次绘制都会调用openPersistentEditor,这会导致不必要的重复操作,甚至可能引发异常。修改为仅在编辑器未打开时调用:

def paint(self, painter, option, index):
    if not self.parent().isPersistentEditorOpen(index):
        self.parent().openPersistentEditor(index)
    super().paint(painter, option, index)

2. 拦截按钮点击的默认提交行为

QPushButton的点击事件会被QItemDelegate识别为编辑完成的信号,从而触发setModelData。通过重写eventFilter拦截该事件,阻止默认逻辑:

def eventFilter(self, editor, event):
    if isinstance(editor, QPushButton) and event.type() == QEvent.MouseButtonRelease:
        return True  # 接管点击事件处理,不触发委托的默认提交
    return super().eventFilter(editor, event)

同时在createEditor中给编辑器安装事件过滤器:

def createEditor(self, parent, option, index):
    editor = QPushButton(parent)
    editor.installEventFilter(self)  # 安装过滤器
    editor.dialog = MyValueEditingDialog()
    # 直接传递editor对象,避免依赖sender()的不确定性
    editor.dialog.accepted.connect(lambda: self.dialog_has_been_closed(editor))
    editor.clicked.connect(editor.dialog.show)
    return editor

3. 可靠触发commitData信号

修改dialog_has_been_closed方法,直接接收editor参数,确保信号发射目标正确:

def dialog_has_been_closed(self, editor):
    self.commitData.emit(editor)

完整修改后的代码

from PyQt5.QtWidgets import QItemDelegate, QPushButton, QDialog
from PyQt5.QtCore import QEvent

class MyDelegate(QItemDelegate):

    def __init__(self, parent):
        super().__init__(parent)

    def createEditor(self, parent, option, index):
        editor = QPushButton(parent)
        editor.installEventFilter(self)
        editor.dialog = MyValueEditingDialog()
        editor.dialog.accepted.connect(lambda: self.dialog_has_been_closed(editor))
        editor.clicked.connect(editor.dialog.show)
        return editor

    def setEditorData(self, editor, index):
        editor.dialog.set_dialog_values(index.data())

    def paint(self, painter, option, index):
        if not self.parent().isPersistentEditorOpen(index):
            self.parent().openPersistentEditor(index)
        super().paint(painter, option, index)

    def setModelData(self, editor, model, index):
        if editor.dialog.result() == QDialog.Accepted:
            data = editor.dialog.get_editor_default_values()
            model.setData(index, data)

    def eventFilter(self, editor, event):
        if isinstance(editor, QPushButton) and event.type() == QEvent.MouseButtonRelease:
            return True
        return super().eventFilter(editor, event)

    def dialog_has_been_closed(self, editor):
        self.commitData.emit(editor)

原理说明

  • 拦截按钮点击事件后,委托不会再将按钮点击视为编辑完成的信号,避免setModelData提前执行。
  • 仅当对话框确认(accepted信号触发)时,才主动发射commitData信号,此时委托才会调用setModelData更新模型。
  • 优化持久编辑器的打开逻辑,避免重复操作导致的异常。

内容的提问来源于stack exchange,提问作者Andrew Plowright

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 09:07:40