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

QStyledItemDelegate使用QWidget作为编辑器时QTableView编辑异常问题

问题核心原因

你当前的实现完全绕开了QStyledItemDelegate的标准编辑生命周期,导致出现一系列异常:

  1. 你手动在按钮点击事件中调用setData写入数据库,但编辑器关闭后,delegate的默认流程还会自动执行setModelData从编辑器取值,此时你的编辑器已经被销毁,取到空值覆盖了model中已更新的数据,所以界面显示为空。
  2. 你把编辑器、index、model等对象存为delegate的实例属性,多次打开编辑会覆盖原有引用,产生野指针,导致后续操作无响应、程序崩溃。
  3. 层层遍历parent获取model和tableview的写法极不稳定,控件层级稍有变化就会报错。
  4. 没有处理表单右上角关闭事件,点击关闭时delegate默认视为提交空数据,导致单元格变空,编辑状态锁死。
  5. 调用model.select()属于暴力刷新,会重置整个model的编辑状态,是治标不治本的方案。

具体修复方案

第一步:封装独立的坐标编辑对话框

单独把geometry编辑功能封装为QDialog子类,自带模态属性和关闭事件处理,避免污染delegate的属性:

class GeomEditDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("编辑坐标")
        formLayout = QtWidgets.QFormLayout(self)
        formLayout.setVerticalSpacing(12)

        # 纬度输入
        self.latEdit = QtWidgets.QLineEdit()
        formLayout.addRow("Latitude", self.latEdit)
        # 经度输入
        self.lngEdit = QtWidgets.QLineEdit()
        formLayout.addRow("Longitude", self.lngEdit)
        # 高程输入
        self.elevationEdit = QtWidgets.QLineEdit()
        formLayout.addRow("Elevation", self.elevationEdit)

        # 按钮组
        self.buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        formLayout.addWidget(self.buttonBox)

        self.resize(200, 300)

    # 注入初始坐标数据
    def set_init_geom(self, geom):
        self.latEdit.setText(str(geom.x))
        self.lngEdit.setText(str(geom.y))
        self.elevationEdit.setText(str(geom.z))

    # 获取编辑后的WKB格式坐标
    def get_result_wkb(self):
        geom_str = f"POINT Z ({self.latEdit.text()} {self.lngEdit.text()} {self.elevationEdit.text()})"
        geom = wkt.loads(geom_str)
        return wkb.dumps(geom, hex=True, srid=4326)

    # 处理右上角关闭事件,等同于点击取消
    def closeEvent(self, event):
        self.reject()
        event.accept()

第二步:重写Delegate的标准生命周期方法

完全遵循delegate的标准流程,不要自己手动管理编辑器生命周期、手动调用setData:

class ValidatedItemDelegate(QStyledItemDelegate):
    def createEditor(self, widget, option, index):
        if not index.isValid():
            return super().createEditor(widget, option, index)
        # 原有其他列的编辑器逻辑保持不变
        if index.column() == 0:
            editor = QtWidgets.QLineEdit(widget)
            validator = QtGui.QRegExpValidator(QtCore.QRegExp('[\w]{1,10}'), editor)
            editor.setValidator(validator)
            return editor
        if index.column() == 2:
            editor = QtWidgets.QSpinBox(widget)
            editor.setMaximum(360)
            editor.setMinimum(1)
            return editor
        # geometry列使用自定义对话框
        if index.column() == 9:
            return GeomEditDialog(widget)
        return super().createEditor(widget, option, index)

    # 给编辑器填充初始值
    def setEditorData(self, editor, index):
        if index.column() == 9 and isinstance(editor, GeomEditDialog):
            raw_data = index.data()
            geom = wkb.loads(bytes.fromhex(raw_data))
            editor.set_init_geom(geom)
            return
        super().setEditorData(editor, index)

    # 将编辑器的数据写回model
    def setModelData(self, editor, model, index):
        if index.column() == 9 and isinstance(editor, GeomEditDialog):
            # 只有点击OK(对话框触发accept)才会执行到此处,取消/关闭不会触发
            wkb_data = editor.get_result_wkb()
            model.setData(index, wkb_data, Qt.EditRole)
            return
        super().setModelData(editor, model, index)

修改后效果

  • 点击OK按钮会自动提交数据,model更新后自动触发视图刷新,不会出现空值
  • 点击Cancel或者右上角关闭按钮,不会提交数据,单元格保持原值
  • 不需要手动调用model.select()刷新,不会出现单元格锁死无法编辑的问题
  • 没有野指针问题,不会出现程序崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 02:36:02