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
相关产品推荐
相关产品推荐

