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

