Pyside2 - Linenumbers en codeeditor incorrecta cuando se cambia de la familia de la fuente/tamaño de la

0

Pregunta

Miré a este editor de código de ejemplo de la oficial dlls de qt5 sitio web https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. Está escrito en C++ pero me implementado en Python usando Pyside2.

El ejemplo de código funciona bien como está, sin embargo, cuando trato de cambiar la familia de la fuente y el tamaño de la QPlainTextEdit las cosas comienzan a llegar desordenados. He intentado modificar un montón de diferentes campos como el uso de la fontMetrics para determinar la altura, etc.

Aquí es un ejemplo mínimo para reproducir el 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_())

Uno de los mayores problemas son los que parece existir un margen superior de desplazamiento en el texto de salida, que yo soy incapaz de obtener dinámicamente en el linenumber widget. Y cuando se ajuste el editor de fuentes para el pintor los números no serán extraídas del mismo tamaño!?

¿Alguien sabe cómo ajustar los números de línea para el mismo nivel horizontal como en el texto correspondiente y también conseguir que sean del mismo tamaño en una forma dinámica, lo que significa que si la fuente va a ser algo más que todos deben ser ajustados automáticamente.

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

Mejor respuesta

1

El problema viene del hecho de que usted está utilizando dos fuentes para diferentes propósitos: el widget de la fuente y el documento fuente.

Cada fuente tiene diferentes aspectos, y su alineación podría ser diferente si consideramos que esas fuentes como base para la elaboración de coordenadas.

Ya que estás dibujando con el documento fuente, pero con el widget de la fuente como referencia, el resultado es que usted tendrá dibujo cuestiones:

  • incluso con el mismo tamaño de punto, de diferentes fuentes son atraídos a diferentes alturas, especialmente si la alineación del texto rectángulo no es correcto (también tenga en cuenta que usted utiliza un incoherente alineación, como Qt.AlignRight | Qt.AlignHCenter se debe considerar siempre la alineación a la derecha y valores predeterminados en la parte superior de la alineación)
  • estás usando el widget de métricas de fuente para que el texto rectángulo de altura, la cual difiere de la del documento métricas, por lo que vas a limitar la altura del dibujo.

A diferencia de otros widgets, editores de texto enriquecido en Qt tienen dos ajustes de fuente:

  • el widget de la fuente;
  • el (valor predeterminado) documento fuente, que puede ser reemplazado por un QTextOption en el documento;

El documento será siempre heredar el widget de la fuente (o fuentes de aplicación) para el valor predeterminado, y así sucederá también cuando la configuración de la fuente para el widget en tiempo de ejecución, e incluso para la aplicación (a menos que una fuente ha establecido de forma explícita para el widget).

Configuración de la fuente para el editor, es por lo general bien para situaciones simples, pero hay que recordar que las fuentes de propagar, de manera que los niños widget heredarán la fuente también.

Por otro lado, la configuración de la fuente por defecto para el documento no se propagan a los niños, pero, como se explicó anteriormente, puede ser anulado por la aplicación de la fuente si que cambia en tiempo de ejecución.

La solución más simple, en su caso, sería establecer la fuente para el editor de widget en lugar del documento. De esta manera usted está seguro de que el LineNumberArea (que es el editor del niño) también heredarán la misma fuente. Con este enfoque, usted ni siquiera necesita para establecer la fuente del pintor, como siempre utilice el widget de la fuente.

En caso de querer utilizar una fuente diferente y aún así mantener la alineación correcta, usted tiene que considerar la posición de referencia de la fuente utilizada para el documento, y el uso que de referencia para la línea de base del widget de la fuente. Con el fin de hacer eso, usted tiene que traducir la posición del bloque, con la diferencia de la ascent() de las dos métricas de fuente.

2021-11-20 13:08:21

En otros idiomas

Esta página está en otros idiomas

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