重写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
相关产品推荐
相关产品推荐

