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

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

核心错误点

代码里四个逻辑错误直接导致边框绘制错位:

  • 光标位置未关联编辑器:创建局部QTextCursor对象修改起止位置后,没有调用setTextCursor()将光标绑定到编辑器实例,cursorRect()始终读取编辑器默认光标的原始位置,和目标文本的起止点完全不对应。
  • 绘制顺序颠倒:paintEvent中先创建QPainter绘制矩形,再调用父类paintEvent,父类重绘文本区时会直接覆盖之前绘制的边框内容。
  • 坐标获取时机过早:在窗口构造函数中获取坐标时,控件尚未完成布局计算、字体渲染、边距适配,拿到的是未初始化的无效坐标值。
  • 计算逻辑鲁棒性差:硬编码矩形高度为10,且未考虑文本跨行场景——如果目标文本折行显示,仅靠起止两点坐标无法拼接出完整的外边框。
修正实现

不需要手动计算起止点坐标差,直接调用QTextCursor自带的selectionRects()方法,该方法会自动返回选区对应所有文本行的精确矩形,自动适配单行、跨行场景,完全不需要手动计算宽高。同时修正绘制顺序、坐标获取时机的问题。

完整可运行代码:

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.highlight_ranges = []

    def add_highlight_box(self, start_pos, end_pos):
        self.highlight_ranges.append((start_pos, end_pos))
        self.viewport().update()

    def paintEvent(self, event):
        # 先执行父类绘制逻辑渲染文本,再绘制边框,避免内容被覆盖
        super().paintEvent(event)
        if not self.highlight_ranges:
            return
        painter = QPainter(self.viewport())
        painter.setPen(QPen(Qt.black, 2, Qt.SolidLine))
        for start, end in self.highlight_ranges:
            cursor = self.textCursor()
            cursor.setPosition(start)
            cursor.setPosition(end, cursor.KeepAnchor)
            # 获取选区对应的所有行矩形,自动适配跨行场景
            for rect in cursor.selectionRects():
                painter.drawRect(rect)


class Window(QWidget):
    def __init__(self):
        super().__init__()
        edit = TextEditor(self)
        layout = QVBoxLayout(self)
        layout.addWidget(edit)
        demo_text = "Hello World, this is a long test text that will wrap to the next line when the window is shrunk, used to verify the multi-line border rendering effect."
        edit.setText(demo_text)
        target_word = "World"
        start_idx = demo_text.find(target_word)
        end_idx = start_idx + len(target_word)
        # 等控件首次显示、布局完成后再添加边框,避免拿到无效坐标
        def on_show(e):
            edit.add_highlight_box(start_idx, end_idx)
            QTextEdit.showEvent(edit, e)
        edit.showEvent = on_show


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 100, 400, 300)
    window.show()
    sys.exit(app.exec_())
实现说明
  • 边框完全贴合文本边缘,不会出现短横线、尺寸异常的错位问题
  • 自动适配窗口缩放、文本换行场景,重绘时边框位置会自动更新
  • 支持同时为多段不连续文本添加外边框,只需要调用add_highlight_box传入对应起止位置即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 23:54:21