PySide2代码编辑器修改字体后行号显示异常如何解决
问题解决方法
核心问题根源
- 仅给
QPlainTextEdit的文档对象设置了自定义字体,没有同步给控件本身设置字体,导致调用editor.font()、editor.fontMetrics()获取到的仍是默认字体参数,行号计算和绘制的尺寸全部错位。 - 行号绘制时的对齐参数错误,
Qt.AlignHCenter(水平居中)和Qt.AlignRight(右对齐)冲突,且垂直方向没有做居中对齐,导致行号和正文不在同一水平线上。 - 循环计算下一行位置时,误用了
blockBoundingGeometry的高度,应该用blockBoundingRect的高度。 - 没有绑定字体变化的触发逻辑,修改编辑器字体后行号不会自动同步更新。
修改后的完整可运行代码
import sys import signal from PySide2.QtCore import Qt, QSize, QRect from PySide2.QtGui import QPaintEvent, QPainter, QColor, QResizeEvent, QFontMetrics from PySide2.QtWidgets import QWidget, QPlainTextEdit, QVBoxLayout, QApplication FONT_SIZE = 20 FONT_FAMILY = 'Source Code Pro' class PlainTextEdit(QPlainTextEdit): def __init__(self, parent=None): super().__init__(parent) self.init_settings_font() def init_settings_font(self): font = self.document().defaultFont() font.setFamily(FONT_FAMILY) font.setFixedPitch(True) font.setPixelSize(FONT_SIZE) # 同时给文档和控件本身设置字体,保持参数一致 self.document().setDefaultFont(font) self.setFont(font) class LineNumberArea(QWidget): def __init__(self, editor): super().__init__(editor) self._editor = editor self._editor.blockCountChanged.connect(lambda new_count: self._update_margin()) self._editor.updateRequest.connect(lambda rect, dy: self._update_request(rect, dy)) # 绑定字体变化信号,字体修改后自动更新行号 self._editor.fontChanged.connect(lambda: (self._update_margin(), self.update())) self._update_margin() def width(self) -> int: _max = max(1000, self._editor.blockCount()) digits = len(f'{_max}') # 用文档的字体计算宽度,保证和正文尺寸匹配 font = self._editor.document().defaultFont() space = QFontMetrics(font).horizontalAdvance('0') * (digits + 1) + 6 return QSize(space, 0).width() def _update_line_geometry(self): content_rect = self._editor.contentsRect() self._update_geometry(content_rect) def _update_geometry(self, content_rect: QRect): self.setGeometry( QRect(content_rect.left(), content_rect.top(), self.width(), content_rect.height()) ) def _update_margin(self): self._editor.setViewportMargins(self.width(), 0, 0, 0) def _update_request(self, rect: QRect, dy: int): if dy: self.scroll(0, dy) else: self.update(0, rect.y(), self.width(), rect.height()) self._update_geometry(self._editor.contentsRect()) if rect.contains(self._editor.viewport().rect()): self._update_margin() # override def resizeEvent(self, event: QResizeEvent) -> None: self._update_line_geometry() # override def paintEvent(self, event: QPaintEvent): painter = QPainter(self) area_color = QColor('darkgrey') painter.fillRect(event.rect(), area_color) visible_block_num = self._editor.firstVisibleBlock().blockNumber() block = self._editor.document().findBlockByNumber(visible_block_num) top = self._editor.blockBoundingGeometry(block).translated(self._editor.contentOffset()).top() bottom = top + self._editor.blockBoundingRect(block).height() active_line_number = self._editor.textCursor().block().blockNumber() + 1 # 直接用文档的字体,和正文完全一致 font = self._editor.document().defaultFont() painter.setFont(font) font_metrics = QFontMetrics(font) while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): number_to_draw = visible_block_num + 1 painter.setPen(QColor('black') if number_to_draw == active_line_number else QColor('white')) # 对齐改为右对齐+垂直居中,解决行号错位问题 painter.drawText( 0, int(top), self.width(), font_metrics.height(), int(Qt.AlignRight | Qt.AlignVCenter), str(number_to_draw) ) block = block.next() top = bottom bottom = top + self._editor.blockBoundingRect(block).height() visible_block_num += 1 painter.end() if __name__ == "__main__": app = QApplication(sys.argv) signal.signal(signal.SIGINT, signal.SIG_DFL) window = QWidget() layout = QVBoxLayout(window) editor = PlainTextEdit() line_num = LineNumberArea(editor) layout.addWidget(editor) window.show() sys.exit(app.exec_())
功能验证
修改后行号和对应文本完全对齐,字号和正文字号保持一致,后续如果需要动态修改编辑器字体,只需要更新init_settings_font方法里的字体参数重新调用即可,行号会自动同步适配。
内容的提问来源于stack exchange,提问作者wasp256
相关产品推荐
相关产品推荐

