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

如何在QTableView中设置选中文本的颜色与样式

QTableView单元格富文本编辑与渲染解决方案

问题需求

在QTableView的指定列实现富文本编辑功能:

  • 支持选中文本设置格式(加粗、斜体、下划线、字号、颜色)
  • 绑定快捷键(如Ctrl+B实现加粗)或工具栏按钮
  • 富文本数据可保存回SQLite数据库,且单元格能正确渲染富文本样式

修改后的完整代码

import sys
import os
import sqlite3

from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


# 初始化数据库(首次运行取消注释)
# con_1 = sqlite3.connect("temp.db")
# con_1.execute('''create table t1(
#    id INTEGER PRIMARY KEY, 'First Name' TEXT, 'Last Name' TEXT, 'Full Name' TEXT);''')
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Wade', 'Wilson', '<b>Wade</b> Wilson');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Norman', 'Osborn', '<i>Norman Osborn</i>');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Stephen', 'Strange', '<span style=\"color:blue; font-size:14pt\">Stephen Strange</span>');")
# con_1.commit()
# con_1.close()

class RichTextDelegate(QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = QTextEdit(parent)
        # 启用富文本编辑(默认已启用,显式设置更明确)
        editor.setAcceptRichText(True)
        
        # 绑定快捷键:Ctrl+B加粗,Ctrl+I斜体,Ctrl+U下划线
        bold_shortcut = QShortcut(QKeySequence("Ctrl+B"), editor)
        bold_shortcut.activated.connect(lambda: self.toggle_format(editor, QTextCharFormat.FontWeight, QFont.Bold))
        
        italic_shortcut = QShortcut(QKeySequence("Ctrl+I"), editor)
        italic_shortcut.activated.connect(lambda: self.toggle_format(editor, QTextCharFormat.FontItalic, True))
        
        underline_shortcut = QShortcut(QKeySequence("Ctrl+U"), editor)
        underline_shortcut.activated.connect(lambda: self.toggle_format(editor, QTextCharFormat.UnderlineStyle, QTextCharFormat.SingleUnderline))
        
        return editor

    def toggle_format(self, editor, format_property, target_value):
        cursor = editor.textCursor()
        if not cursor.hasSelection():
            return
        
        fmt = QTextCharFormat()
        current_value = cursor.charFormat().property(format_property)
        
        # 切换格式:如果当前是目标值则取消,否则设置
        if current_value == target_value:
            if format_property == QTextCharFormat.FontWeight:
                fmt.setFontWeight(QFont.Normal)
            elif format_property == QTextCharFormat.FontItalic:
                fmt.setFontItalic(False)
            elif format_property == QTextCharFormat.UnderlineStyle:
                fmt.setUnderlineStyle(QTextCharFormat.NoUnderline)
        else:
            if format_property == QTextCharFormat.FontWeight:
                fmt.setFontWeight(target_value)
            elif format_property == QTextCharFormat.FontItalic:
                fmt.setFontItalic(target_value)
            elif format_property == QTextCharFormat.UnderlineStyle:
                fmt.setUnderlineStyle(target_value)
        
        cursor.mergeCharFormat(fmt)
        editor.setTextCursor(cursor)

    def setEditorData(self, editor, index):
        # 从模型加载HTML格式的富文本
        html_content = index.data(Qt.DisplayRole) or ""
        editor.setHtml(html_content)

    def setModelData(self, editor, model, index):
        # 将编辑器的HTML内容保存回模型(最终存入数据库)
        html_content = editor.toHtml()
        model.setData(index, html_content, Qt.EditRole)

    def paint(self, painter, option, index):
        # 自定义绘制富文本内容
        html_content = index.data(Qt.DisplayRole) or ""
        doc = QTextDocument()
        doc.setHtml(html_content)
        
        # 调整文档大小以适配单元格
        doc.setTextWidth(option.rect.width())
        
        painter.save()
        # 设置绘制区域,避免内容超出单元格
        painter.translate(option.rect.topLeft())
        doc.drawContents(painter)
        painter.restore()
        
        # 绘制单元格选中状态的边框(保留默认选中样式)
        if option.state & QStyle.State_Selected:
            painter.save()
            pen = QPen(option.palette.highlight(), 1)
            painter.setPen(pen)
            painter.drawRect(option.rect.adjusted(0, 0, -1, -1))
            painter.restore()

class TableViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        working_dir = os.getcwd()
        db_path = f"{working_dir}/temp.db"
        print(db_path)
        
        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName(db_path)
        if not self.db.open():
            print("数据库打开失败:", self.db.lastError().text())
            sys.exit(1)

        self.model = QSqlTableModel(self, self.db)   
        self.model.setTable('t1')
        # 设置编辑策略为手动提交(可选,根据需求调整)
        self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.model.select()

        self.tableView = QTableView(self)
        self.tableView.setModel(self.model)
        # 调整列宽以适应内容
        self.tableView.resizeColumnsToContents()
        # 设置行高以显示多行富文本
        self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        
        # 为第3列(Full Name)设置富文本委托
        self.tableView.setItemDelegateForColumn(3, RichTextDelegate())
        
        # 添加工具栏(可选,用于格式设置按钮)
        toolbar = self.addToolBar("格式工具")
        bold_btn = QAction(QIcon(), "加粗", self)
        bold_btn.setShortcut("Ctrl+B")
        bold_btn.triggered.connect(lambda: self.apply_format_to_current_editor(QTextCharFormat.FontWeight, QFont.Bold))
        toolbar.addAction(bold_btn)
        
        italic_btn = QAction(QIcon(), "斜体", self)
        italic_btn.setShortcut("Ctrl+I")
        italic_btn.triggered.connect(lambda: self.apply_format_to_current_editor(QTextCharFormat.FontItalic, True))
        toolbar.addAction(italic_btn)
        
        underline_btn = QAction(QIcon(), "下划线", self)
        underline_btn.setShortcut("Ctrl+U")
        underline_btn.triggered.connect(lambda: self.apply_format_to_current_editor(QTextCharFormat.UnderlineStyle, QTextCharFormat.SingleUnderline))
        toolbar.addAction(underline_btn)

        layout = QVBoxLayout()
        layout.addWidget(self.tableView)
        
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        
        self.setWindowTitle("QTableView富文本编辑器")
        self.resize(800, 400)
        self.show()

    def apply_format_to_current_editor(self, format_property, target_value):
        # 获取当前正在编辑的单元格编辑器
        index = self.tableView.currentIndex()
        if index.column() != 3:
            return
        
        editor = self.tableView.indexWidget(index)
        if isinstance(editor, QTextEdit):
            delegate = self.tableView.itemDelegateForColumn(3)
            delegate.toggle_format(editor, format_property, target_value)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    tv = TableViewer()
    sys.exit(app.exec_())

关键实现说明

1. 富文本数据存储

  • 将单元格内容以HTML格式存入SQLite数据库,数据库的TEXT类型可直接存储HTML字符串,确保富文本样式信息不会丢失。
  • 首次初始化数据库时,插入的示例数据已包含HTML标签,用于测试渲染效果。

2. 委托核心修改

  • createEditor:创建QTextEdit编辑器并绑定快捷键,实现选中文本的格式切换。
  • setEditorData/setModelData:替换原纯文本逻辑,改为加载和保存HTML内容,确保富文本数据在编辑和存储间正确流转。
  • paint方法:不再依赖默认的纯文本绘制,而是使用QTextDocument解析HTML并绘制到单元格,同时保留选中状态的边框绘制,保证视觉一致性。

3. 格式操作逻辑

  • toggle_format方法实现格式切换:检测选中文本的当前格式,若已设置则取消,否则应用目标格式(如Ctrl+B在加粗和正常之间切换)。
  • 工具栏按钮与快捷键功能一致,方便鼠标操作。

4. 单元格适配

  • 开启行高自动调整(ResizeToContents),确保多行富文本内容能完整显示。
  • QTextDocument设置文本宽度为单元格宽度,避免内容超出单元格边界。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:40:59