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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:15:03