Pyside2 - Linenumbers v codeeditor nesprávne, keď sa zmeniť font family/veľkosť

0

Otázka

Pozrel som sa na tento kód editor príklad z oficiálnych Qt5 webové stránky https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. Je napísaný v C++, ale ja ho zaviedli v Pythone pomocou Pyside2.

Napríklad kód funguje tak, ako je, avšak, keď som sa pokúsiť zmeniť rodina písma a veľkosť QPlainTextEdit veci sa začnú dostať chaotický. Snažil som sa meniť veľa rôznych oblastiach, ako pomocou fontMetrics na určenie výšky atď.

Tu je minimálnym príklad na reprodukovanie 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 najväčších problémov je, že sa zdá byť horný okraj kompenzované vo forme obyčajného textu východ, ktorý som schopný dynamicky dostať v linenumber widget. A keď nastavenie editora font na maliar čísla nebudú vypracované rovnakej veľkosti!?

Vie niekto ako nastaviť čísla riadkov, na rovnakej horizontálnej úrovni ako príslušný text a aj sa im byť rovnakej veľkosti v dynamickým spôsobom, to znamená, že ak písmo bude nastavený na niečo iné, všetky by mali byť nastavená automaticky.

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

Najlepšiu odpoveď

1

Problém pochádza zo skutočnosti, že používate dve písma pre rôzne účely: the widget písma a dokument písma.

Každé písmo má rôzne aspekty, a jeho zosúladenie môžu líšiť ak ste zvážiť tieto písma ako základu pre čerpanie súradnice.

Od kreslení s dokladom písma, ale používajú widget písma, ako odkaz, výsledkom je, že budete mať kreslenie otázky:

  • dokonca s rovnakým bod veľkosti, rôzne fonty sú vypracované v rôznych výškach, najmä ak zarovnanie textu obdĺžnik nie je správne (všimnite si, že ste použili nekonzistentné zarovnanie, ako Qt.AlignRight | Qt.AlignHCenter bude vždy zvážiť právo zarovnanie a predvolené top zarovnanie)
  • používate widget font metriky ak chcete nastaviť text obdĺžnik výška, ktorá sa líši od dokumentu metriky, aby ste si limit na výšku čerpania.

Na rozdiel od iných vecičiek, bohaté textové editory v Qt majú dve nastavenia písma:

  • v widget písma;
  • (predvolené) dokument písma, ktorá môže byť prepísaná QTextOption v dokumente;

Dokument bude vždy zdediť widget písmo (alebo aplikácia písma) pre predvolenú, a tak sa aj stalo, keď sa nastavenie veĺkosti písma pre widget za behu, a dokonca aj aplikácie (pokiaľ font bol výslovne stanovené pre widget).

Nastavenie veĺkosti písma pre editor je zvyčajne v poriadku pre jednoduché situácie, ale musíte mať na pamäti, že písma šíria, takže deti widget zdedí písmo príliš.

Na druhej strane, nastavenie predvolené písmo pre dokument sa nerozšíria na deti, ale, ako je uvedené vyššie, môže byť prepísaná aplikácie, písma, či je to meniť za behu.

Najjednoduchšie riešenie, vo vašom prípade, by bolo nastavenie veĺkosti písma pre editor widget namiesto dokumentu. Týmto spôsobom ste sa uistili, že LineNumberArea (čo je editor dieťaťa) bude tiež zdedia rovnaké písmo. S týmto prístupom nemáte ešte potrebovať na nastavenie písma maliar, ako to bude vždy použiť widget písma.

V prípade, že chcete použiť iný typ písma a stále správne zarovnanie, budete musieť zvážiť, východiskovej pozície písma použitého dokumentu, použite tento odkaz pre východiskový widget písma. S cieľom urobiť, budete musieť preložiť blok pozícia so rozdiel ascent() z dvoch font metriky.

2021-11-20 13:08:21

V iných jazykoch

Táto stránka je v iných jazykoch

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