Pyside2 - Linenumbers în codeeditor incorecte, atunci când s-a schimbat familia de fonturi/dimensiuni

0

Problema

M-am uitat la acest editor de cod exemplu de la oficial Qt5-ul https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. Este scris în C++, dar am pus-o în aplicare în Python folosind Pyside2.

De exemplu, codul funcționează bine ca este, cu toate acestea, atunci când am încerca să schimbe familia de fonturi și dimensiunea QPlainTextEdit lucrurile încep să devină murdar. Am încercat să tweak o mulțime de diferite domenii, cum ar fi utilizarea fontMetrics pentru a determina la înălțime etc.

Aici este un minim exemplu pentru a reproduce problema

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_())

Una dintre cele mai mari probleme sunt că nu pare a fi un top marja de offset în plaintext ieșire care sunt în imposibilitatea de a obține în mod dinamic în linenumber widget. Și când setarea fontului editor pentru a pictorului numerele nu vor fi trase la aceeași dimensiune!?

Stie cineva cum se poate regla numerele de linie la același nivel orizontal ca textul corespunzător și, de asemenea, obține-le să fie de aceeași dimensiune într-un mod dinamic, în sensul că, dacă fontul va fi setat la altceva, acestea ar trebui să fie ajustate în mod automat.

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

Cel mai bun răspuns

1

Problema vine din faptul că te folosești de două fonturi pentru diferite scopuri: widget font și document font.

Fiecare font are diferite aspecte, și alinierea acesteia s-ar putea să difere dacă ia în considerare acele fonturi ca baza pentru desen coordonatele.

Când ești desen cu documentul font dar utilizați widget-ul de font ca referință, rezultatul este că veți avea de desen probleme:

  • chiar și cu același punct dimensiuni, fonturi diferite sunt trase la înălțimi diferite, mai ales dacă alinierea textului dreptunghi nu este corect (de asemenea, rețineți că ai folosit-o contradicție de aliniere, ca Qt.AlignRight | Qt.AlignHCenter va lua în considerare întotdeauna dreptul de aliniere și implicit la partea de sus aliniere)
  • vă că utilizați widget-ul de font valori pentru a seta textul dreptunghi de înălțime, care diferă de document valori, așa că va limita înălțimea de desen.

Spre deosebire de alte widget-uri, editoare de text bogate în Qt două setări de font:

  • la widget font;
  • (implicit) document fontului, care poate fi înlocuită de o QTextOption în document;

Documentul va mereu moșteni fontului widget (sau aplicație font) pentru implicit, și acest lucru se va întâmpla, de asemenea, atunci când setarea fontului pentru widget la runtime, și chiar și pentru aplicații (dacă nu un font a fost stabilit în mod explicit pentru widget).

Setarea fontului pentru editor este, de obicei bine pentru situații simple, dar trebuie să ne amintim că fonturi propaga, astfel încât copiii widget va moșteni fontul prea.

Pe de altă parte, setarea implicită a fontului pentru document nu se vor propaga la copii, dar, cum am explicat mai sus, poate fi înlocuită prin aplicarea font cazul în care s-a schimbat la runtime.

Cea mai simplă soluție, în cazul tău, ar fi pentru a seta fontul pentru editor widget în loc de document. În acest fel ești sigur că LineNumberArea (care este editor), copilul va moșteni, de asemenea, același font. Cu această abordare nu aveți nevoie, chiar să setați fontul de pictor, ca întotdeauna se va folosi widget-ul de font.

În cazul în care doriți să utilizați un font diferit și încă mai păstrează alinierea corectă, trebuie să ia în considerare de bază poziția de fontul utilizat pentru document, și de a folosi ca referință de bază a fontului widget. Pentru a face asta, trebuie să traducă bloc poziție cu diferenta de ascent() de două font valori.

2021-11-20 13:08:21

În alte limbi

Această pagină este în alte limbi

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