QTableView中HTML编辑器样式统一及只读可选功能实现问题
解决方案
1. 编辑器边框与边距问题
你找到的去边框方案正确,边距问题不要使用setContentMargins,直接对齐编辑器内部文档和渲染用文档的边距,同时关闭滚动条即可:
# 在createEditor初始化编辑器时添加以下代码 editor.setFrameStyle(QtWidgets.QFrame.NoFrame) editor.document().setDocumentMargin(0) # 和paint、sizeHint中的文档边距完全一致 editor.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) editor.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
2. 列0只读但支持选中快捷键
setReadOnly(True)会默认过滤部分文本操作快捷键,建议自定义只读文本框,只拦截修改内容的操作,保留所有选中、导航、复制相关快捷键:
class ReadOnlyTextEdit(QtWidgets.QTextEdit): def keyPressEvent(self, event): # 允许的修饰键组合 allowed_modifiers = ( QtCore.Qt.Key_Control | QtCore.Qt.Key_Shift, QtCore.Qt.Key_Control, QtCore.Qt.Key_Shift, QtCore.Qt.NoModifier ) # 允许的按键:导航、选中、复制 allowed_keys = ( QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Home, QtCore.Qt.Key_End, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown, QtCore.Qt.Key_C ) if (event.modifiers() in allowed_modifiers and event.key() in allowed_keys) or event.text().isspace(): super().keyPressEvent(event) else: # 忽略所有修改内容的按键操作 event.ignore() def insertFromMimeData(self, source): # 禁止粘贴操作 pass
列0创建编辑器时直接使用上述自定义类即可,无需设置setReadOnly(True),所有选中类快捷键均可正常使用。
3. 长文本换行不一致问题
该问题根源有两个:
- 复用全局QTextDocument实例,不同单元格计算尺寸和渲染时互相干扰
- 编辑器初始化时先设置HTML再设置宽度,导致第一次换行计算错误,第二次进入编辑时宽度正确所以换行恢复正常
修复代码
class TableViewDelegate(QtWidgets.QStyledItemDelegate): def __init__(self): super().__init__() self.editor_for_col_0 = None self.editor_for_other_cols = None def createEditor(self, parent, option, index): if index.column() == 0: if self.editor_for_col_0 is None: self.editor_for_col_0 = ReadOnlyTextEdit(parent) self.editor_for_col_0.setFrameStyle(QtWidgets.QFrame.NoFrame) self.editor_for_col_0.document().setDocumentMargin(0) self.editor_for_col_0.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.editor_for_col_0.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) return self.editor_for_col_0 else: if self.editor_for_other_cols is None: self.editor_for_other_cols = QtWidgets.QTextEdit(parent) self.editor_for_other_cols.setFrameStyle(QtWidgets.QFrame.NoFrame) self.editor_for_other_cols.document().setDocumentMargin(0) self.editor_for_other_cols.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.editor_for_other_cols.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) return self.editor_for_other_cols def setEditorData(self, editor, index): cell_text = index.model().data(index, QtCore.Qt.DisplayRole) # 先设置文档宽度再设置HTML,保证换行计算正确 editor.document().setTextWidth(editor.width()) editor.setHtml(cell_text) def updateEditorGeometry(self, editor, option, index): # 先设置编辑器尺寸和单元格完全一致 editor.setGeometry(option.rect) # 同步文档宽度和单元格宽度 editor.document().setTextWidth(option.rect.width()) super().updateEditorGeometry(editor, option, index) def paint(self, painter, option, index): options = QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options, index) # 每次渲染新建文档,避免全局实例互相干扰 doc = QtGui.QTextDocument() doc.setDefaultFont(options.font) doc.setDocumentMargin(0) doc.setTextWidth(options.rect.width()) doc.setHtml(options.text) painter.save() options.text = "" options.widget.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter) painter.translate(options.rect.left(), options.rect.top()) clip = QtCore.QRectF(0, 0, options.rect.width(), options.rect.height()) painter.setClipRect(clip) ctx = QtGui.QAbstractTextDocumentLayout.PaintContext() ctx.clip = clip doc.documentLayout().draw(painter, ctx) painter.restore() def sizeHint(self, option, index): self.initStyleOption(option, index) if not option.text: return super().sizeHint(option, index) # 每次计算尺寸新建文档,避免干扰 doc = QtGui.QTextDocument() doc.setDefaultFont(option.font) doc.setDocumentMargin(0) col_width = option.rect.width() if option.rect.width() > 0 else self.parent().columnWidth(index.column()) doc.setTextWidth(col_width) doc.setHtml(option.text) return QtCore.QSize(int(doc.idealWidth()), int(doc.size().height()))
4. 初始化自动进入编辑问题
AllEditTriggers包含了CurrentChanged触发条件,当前项初始化变更时就会自动进入编辑,修改触发规则为你需要的组合即可:
# TableView的__init__中替换setEditTriggers代码 self.setEditTriggers( QtWidgets.QAbstractItemView.DoubleClicked | QtWidgets.QAbstractItemView.EditKeyPressed | QtWidgets.QAbstractItemView.SelectedClicked )
内容的提问来源于stack exchange,提问作者mike rodent
相关产品推荐
相关产品推荐

