Pyside2 - numéros de lignes dans codeeditor incorrect lors de changé de famille de police/taille

0

La question

J'ai regardé cet éditeur de code exemple de l'officiel Qt5 site web https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. Il est écrit en C++ mais j'ai implémenté en Python à l'aide de Pyside2.

L'exemple de code fonctionne très bien comme il est, cependant, lorsque je tente de changer la police de caractères de la famille et de la taille de la QPlainTextEdit les choses commencent à se salir. J'ai essayé de bidouiller tout un tas de domaines différents comme l'utilisation de la fontMetrics pour déterminer la hauteur etc.

Voici un exemple minimal de reproduire le problème

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

Une des plus grandes questions sont qu'il semble y avoir une marge supérieure de décalage dans le texte en clair à la sortie de laquelle je ne suis pas en mesure d'obtenir dynamiquement dans le linenumber widget. Et lors de la configuration de l'éditeur de polices pour le peintre, les numéros ne seront pas dessinés de la même taille!?

Quelqu'un sait-il comment régler les numéros de ligne pour le même plan horizontal que le texte correspondant et aussi les amener à être de la même taille dans une manière dynamique, ce qui signifie que si la police va être autre chose qu'ils devraient tous être ajustés automatiquement.

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

La meilleure réponse

1

Le problème vient du fait que vous êtes à l'aide de deux polices de caractères à des fins différentes: le widget de police et le document de la police.

Chaque police a différents aspects, et son alignement peuvent différer si vous considérez les polices en tant que base pour le dessin coordonnées.

Puisque vous êtes le dessin avec le document de la police, mais en utilisant le widget de police à titre de référence, le résultat est que vous allez avoir des problèmes de dessin:

  • même avec la même taille, les différentes polices de caractères sont dessinés à différentes hauteurs, en particulier si l'alignement du texte rectangle n'est pas correcte (voir aussi la note que vous avez utilisé des incohérences dans l'alignement, Qt.AlignRight | Qt.AlignHCenter considérons toujours l'alignement à droite et à défaut d'alignement en haut)
  • vous utilisez le widget des mesures de police pour définir le texte rectangle de hauteur, qui diffère de celui du document métriques, de sorte que vous aurez la limite de la hauteur du dessin.

Contrairement à d'autres widgets, éditeurs de texte riches en Qt deux paramètres de police:

  • le widget de police;
  • l' (par défaut) de document de la police, qui peut être remplacée par une QTextOption dans le document;

Le document sera toujours hériter le widget de police (ou de l'application de la police) pour la valeur par défaut, et ce sera également se produire lors de la configuration de la police pour le widget à l'exécution, et même de l'application (sauf une police a été explicitement définie pour le widget).

Réglage de la police de l'éditeur est généralement très bien pour des situations simples, mais vous devez vous rappeler que les polices de se propager, de sorte que les enfants widget héritera de la police aussi.

D'autre part, la définition de la police par défaut pour le document ne sera pas propagée aux enfants, mais, comme expliqué ci-dessus, peut être remplacée par l'application de la police si cela a changé au moment de l'exécution.

La solution la plus simple, dans votre cas, serait de définir la police de l'éditeur widget au lieu du document. De cette façon, vous êtes sûr que la LineNumberArea (qui est l'éditeur de l'enfant) sera aussi hériter de la même police. Avec cette approche, vous n'avez même pas besoin de définir la police de la peintre, comme il le fera toujours utiliser le widget de police.

Dans le cas où vous souhaitez utiliser une police différente et de toujours maintenir un alignement correct, vous devez tenir compte de la position de référence de la police utilisée pour le document, et d'utiliser cette référence pour la base de référence du widget de police. Pour ce faire, vous avez à traduire la position du bloc avec la différence de l' ascent() des deux mesures de police.

2021-11-20 13:08:21

Dans d'autres langues

Cette page est dans d'autres langues

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