QAbstractTextDocumentLayout.PaintContext在PySide2中选区绘制失效求助
解决PySide2中QAbstractTextDocumentLayout.PaintContext缺失selections属性的选区高亮问题
针对PySide2环境下自定义日志控制台遇到的选区高亮失效问题,由于官方绑定遗漏了QAbstractTextDocumentLayout.PaintContext.selections属性,可通过手动绘制选区高亮的方式替代,具体实现如下:
核心思路
绕过缺失的selections属性,在paintEvent中先绘制QTextDocument的基础内容,再根据当前选区范围手动绘制高亮背景。需自行维护选区的起始/结束光标位置,并处理滚动偏移对选区坐标的影响。
代码实现(基于最小可复现示例修改)
from PySide2.QtWidgets import QWidget, QApplication from PySide2.QtGui import QTextDocument, QPainter, QTextCursor, QPalette from PySide2.QtCore import Qt, QRectF class CustomTextWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.doc = QTextDocument() self.doc.setPlainText("\n".join([f"Line {i}" for i in range(1000)])) # 维护选区光标 self.selection_cursor = QTextCursor(self.doc) self.selection_cursor.clearSelection() # 滚动偏移量 self.scroll_offset = 0 def paintEvent(self, event): painter = QPainter(self) painter.setClipRect(event.rect()) # 绘制文档基础内容 ctx = QTextDocument.PaintContext() self.doc.drawContents(painter, QRectF(0, -self.scroll_offset, self.width(), self.doc.size().height())) # 手动绘制选区高亮 if self.selection_cursor.hasSelection(): highlight_color = self.palette().color(QPalette.Highlight) painter.setBrush(highlight_color) painter.setPen(Qt.NoPen) # 遍历选区覆盖的所有文本块 start_block = self.selection_cursor.block() end_block = self.selection_cursor.selectionEnd().block() current_block = start_block while current_block.isValid(): block_layout = current_block.layout() block_rect = block_layout.boundingRect() block_y = current_block.blockNumber() * block_rect.height() - self.scroll_offset # 处理起始块的部分选区 if current_block == start_block: start_x = block_layout.positionForTextPosition(self.selection_cursor.positionInBlock()).x() if current_block == end_block: end_x = block_layout.positionForTextPosition(self.selection_cursor.selectionEnd().positionInBlock()).x() painter.drawRect(start_x, block_y, end_x - start_x, block_rect.height()) else: painter.drawRect(start_x, block_y, self.width() - start_x, block_rect.height()) # 处理结束块的部分选区 elif current_block == end_block: end_x = block_layout.positionForTextPosition(self.selection_cursor.selectionEnd().positionInBlock()).x() painter.drawRect(0, block_y, end_x, block_rect.height()) # 处理中间完整文本块 else: painter.drawRect(0, block_y, self.width(), block_rect.height()) current_block = current_block.next() def mousePressEvent(self, event): # 转换鼠标位置到文档坐标系 doc_pos = event.pos() + Qt.Point(0, self.scroll_offset) cursor = QTextCursor(self.doc) cursor.setPosition(self.doc.documentLayout().hitTest(doc_pos.toPointF(), Qt.FuzzyHit)) self.selection_cursor = cursor self.selection_cursor.beginEditBlock() self.update() def mouseMoveEvent(self, event): if self.selection_cursor.hasSelection(): doc_pos = event.pos() + Qt.Point(0, self.scroll_offset) cursor = QTextCursor(self.doc) cursor.setPosition(self.doc.documentLayout().hitTest(doc_pos.toPointF(), Qt.FuzzyHit)) self.selection_cursor.setPosition(cursor.position(), QTextCursor.KeepAnchor) self.update() def mouseReleaseEvent(self, event): self.selection_cursor.endEditBlock() self.update() def resizeEvent(self, event): self.doc.setTextWidth(self.width()) super().resizeEvent(event) if __name__ == "__main__": app = QApplication([]) w = CustomTextWidget() w.resize(400, 300) w.show() app.exec_()
关键细节说明
- 选区维护:通过
QTextCursor记录选区的起始和结束位置,在鼠标事件中实时更新选区范围。 - 坐标转换:滚动偏移量需叠加到鼠标位置,才能正确映射到文档中的实际位置。
- 分块绘制:针对跨多行的选区,逐个处理每个文本块的部分或完整高亮,确保选区显示准确。
这种方式完全绕过了PaintContext.selections的依赖,在PySide2中可正常实现选区高亮,同时保留原有的大文本处理性能优势。
内容的提问来源于stack exchange,提问作者Sneyek
相关产品推荐
相关产品推荐

