Pyside2 - Linenumbers v codeeditor nesprávné, když se změnil rodina písma/velikost

0

Otázka

Podíval jsem se na tento editor kódu, například z oficiální Qt5 webové stránky https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. Je napsán v C++, ale jsem implementoval v jazyce Python s využitím Pyside2.

Příklad kódu funguje tak, jak je, nicméně, když se snažím změňte rodinu písma a velikost QPlainTextEdit věci začnou dostat chaotický. Snažil jsem se to vyladit spoustu různých oblastech, jako je použití fontMetrics k určení výšky atd.

Zde je minimální příklad pro reprodukci problému

import sys
import signal
from PySide2.QtCore import Qt, QSize, QRect
from PySide2.QtGui import QPaintEvent, QPainter, QColor, QResizeEvent
from PySide2.QtWidgets import QWidget, QPlainTextEdit, QVBoxLayout
from PySide2 import QtCore
from PySide2.QtWidgets import 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)


class LineNumberArea(QWidget):
    TMP = dict()

    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._update_margin()

    def width(self) -> int:
        # we use 1000 as a default size, so from 0-9999 this length will be applied
        _max = max(1000, self._editor.blockCount())
        digits = len(f'{_max}')
        space = self._editor.fontMetrics().horizontalAdvance('0', -1) * (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):
        self._update(0, rect.y(), self.width(), rect.height(), self._editor.contentsRect())

        if rect.contains(self._editor.viewport().rect()):
            self._update_margin()

    def _update(self, x: int, y: int, w: int, h: int, content_rect: QRect):
        self.update(x, y, w, h)
        self._update_geometry(content_rect)

    # override
    def resizeEvent(self, event: QResizeEvent) -> None:
        self._update_line_geometry()

    # override
    def paintEvent(self, event: QPaintEvent):
        painter = QPainter(self)
        area_color = QColor('darkgrey')

        # Clearing rect to update
        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_size = storage.get_setting(Constants.Editor_font_size).value
        font = self._editor.font()

        while block.isValid() and top <= event.rect().bottom():
            if block.isVisible() and bottom >= event.rect().top():
                number_to_draw = visible_block_num + 1

                if number_to_draw == active_line_number:
                    painter.setPen(QColor('black'))
                else:
                    painter.setPen(QColor('white'))

                font.setPixelSize(self._editor.document().defaultFont().pixelSize())
                painter.setFont(font)

                painter.drawText(
                    -5,
                    top,
                    self.width(),
                    self._editor.fontMetrics().height(),
                    int(Qt.AlignRight | Qt.AlignHCenter),
                    str(number_to_draw)
                )

            block = block.next()
            top = bottom
            bottom = top + self._editor.blockBoundingGeometry(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()
    editor = PlainTextEdit()
    line_num = LineNumberArea(editor)

    layout.addWidget(editor)
    window.setLayout(layout)

    window.show()

    sys.exit(app.exec_())

Jedním z největších problémů je, že se zdá být horní okraj-odsazení v textu výstupu, který jsem schopen dynamicky se v linenumber widget. A při nastavení editoru písmo malíř čísla nebudou kresleny stejnou velikost!?

Ví někdo, jak upravit čísla řádků na stejné horizontální úrovni jako odpovídající text a také přimět je, aby měly stejnou velikost v dynamickým způsobem, což znamená, že pokud písmo bude nastaven na něco jiného, všichni by měli být upravena automaticky.

pyside2 python-3.x
2021-11-20 05:34:22
1

Nejlepší odpověď

1

Problém vychází ze skutečnosti, že používáte dva fonty pro různé účely: widgetu písmo a dokumentu písmo.

Každé písmo má různé aspekty, a jeho vyrovnání se může lišit, pokud se domníváte, ty písma jako základ pro kreslení souřadnice.

Když jste kreslení s dokumentem písmo, ale pomocí widgetu písmo jako referenční, výsledkem je, že budete mít výkres problémů:

  • i při stejné bodové velikosti, různé fonty jsou vypracovány v různých výškách, a to zejména pokud zarovnání textu obdélník není správné (všimněte si také, že jste použili nekonzistentní zarovnání, jako Qt.AlignRight | Qt.AlignHCenter vždy zvažte, správné zarovnání a výchozí top zarovnání)
  • používáte widget písmo metriky, které chcete nastavit text, obdélník výška, které se liší od dokumentu metriky, takže budete omezit výšku výkresu.

Na rozdíl od jiných widgety, rich text editory v Qt mít dvě nastavení písma:

  • na widgetu písmo;
  • (výchozí) dokumentu písmo, které může být přepsána pomocí QTextOption v dokumentu;

Dokument bude vždy zdědí widget písmo (font) pro výchozí nastavení, a to bude také dojít, pokud nastavení písma pro widget v běhu, a to i pro aplikace (pokud to písmo výslovně nastavit na widget).

Nastavení písma pro editor je obvykle v pořádku pro jednoduché situace, ale musíš si uvědomit, že písma množit, takže děti widget zdědí písmo.

Na druhou stranu, nastavit výchozí písmo pro dokument není šíření dětem, ale, jak bylo vysvětleno výše, může být přepsána aplikace písma, pokud se to změnilo v běhu.

Nejjednodušší řešení ve vašem případě, by bylo nastavit písma pro editor widget místo toho dokumentu. Tímto způsobem jste si jisti, že LineNumberArea (což je editor je dítě), bude také zdědí stejné písmo. S tímto přístupem se ani nemusíte nastavit písmo malíře, jako to bude vždy používat písma widget.

V případě, že chcete použít jiné písmo a stále udržovat správné zarovnání, budete muset vzít v úvahu výchozí pozici písmo použité pro dokument, a použít odkaz na účaří písma widget. Aby bylo možné udělat to, že budete muset přeložit blok pozici s rozdílem ascent() ze dvou font metrics.

2021-11-20 13:08:21

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................