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

重写QTextEdit的paintEvent绘制文字矩形边框时坐标错误

问题根因
  • 绘制顺序错误:原代码先执行自定义矩形绘制,再调用父类paintEvent,父类绘制的文本、背景会直接覆盖自定义绘制内容,之前看到的短划线只是4px粗笔刷没被完全覆盖的边缘。
  • 坐标获取逻辑错误:无参调用cursorRect()时,返回的是QTextEdit当前激活光标的位置,不是你手动修改了position的临时cursor的位置。要获取指定光标对应的矩形,必须调用带QTextCursor参数的重载版本cursorRect(cursor)。
  • 坐标获取时机错误:在窗口__init__阶段控件还未完成布局渲染,字体、边距、文本排版都没完成计算,此时拿到的是控件初始化的默认坐标,和文本实际渲染位置偏差极大。
  • 尺寸硬编码错误:直接写死矩形高度为10,无法适配不同字号、不同行间距的文本显示,很容易出现框线和文字错位的问题。
修复后代码
import sys
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtWidgets import QTextEdit, QWidget, QApplication, QVBoxLayout
from PyQt5.QtCore import Qt


class TextEditor(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.coordinates = []

    def paintEvent(self, event):
        # 先调用父类方法完成文本、背景的基础绘制,避免自定义内容被覆盖
        super(TextEditor, self).paintEvent(event)
        painter = QPainter(self.viewport())
        painter.setPen(QPen(Qt.black, 2, Qt.SolidLine))
        if self.coordinates:
            for start_pos, end_rect in self.coordinates:
                # 直接取文本行的实际高度,适配不同字号、行间距
                painter.drawRect(
                    start_pos.x(),
                    start_pos.y(),
                    end_rect.x() - start_pos.x(),
                    end_rect.height()
                )

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.edit = TextEditor(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.edit)
        self.edit.setText("Hello World")
        self.target_word = "World"

    def showEvent(self, event):
        # 等控件首次显示、布局渲染完成后再计算坐标,拿到准确的文本位置
        super().showEvent(event)
        text = self.edit.toPlainText()
        start = text.find(self.target_word)
        end = start + len(self.target_word)
        self.edit.coordinates.append(self.get_target_rect(start, end))
        self.edit.viewport().update()

    def get_target_rect(self, start, end):
        cursor = self.edit.textCursor()
        cursor.setPosition(start)
        # 传入自定义光标,获取对应位置的准确矩形
        start_rect = self.edit.cursorRect(cursor)
        cursor.setPosition(end)
        end_rect = self.edit.cursorRect(cursor)
        return (start_rect.topLeft(), end_rect)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 100, 1000, 1000)
    window.show()
    sys.exit(app.exec_())

说明:上述实现适配单行连续文本加外框的场景,如果需要给跨多行的选中内容加外框,需要遍历选中范围内的所有文本行,逐行计算矩形坐标后分别绘制,避免出现覆盖大量空白区域的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:45:44