Pyside2 - Linenumbers in comprimibile non corretto quando si modifica il tipo di carattere/dimensione

0

Domanda

Ho guardato questo editor di codice di esempio dal sito ufficiale Qt5 sito https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html. È scritto in C++, ma l'ho implementato in Python utilizzando Pyside2.

Il codice di esempio funziona bene come è, tuttavia, quando provo a cambiare la famiglia di font e la dimensione del QPlainTextEdit le cose iniziano a ottenere disordinato. Ho cercato di ottimizzare un sacco di diversi settori, come l'uso di fontMetrics per determinare l'altezza.

Qui è un esempio minimale per riprodurre il 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 dei principali problemi è che sembra che ci sia un margine superiore di offset in testo normale uscita che io sono in grado di ottenere dinamicamente il numero di widget. E quando si imposta l'editor di font e il pittore i numeri non verranno tracciate le stesse dimensioni?

Qualcuno sa come modificare i numeri di riga, per lo stesso livello orizzontale di testo corrispondenti e anche ottenere la stessa dimensione in modo dinamico, il che significa che se il font sarà qualcos'altro che dovrebbe essere regolati automaticamente.

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

Migliore risposta

1

Il problema deriva dal fatto che si stanno utilizzando due tipi di carattere per scopi diversi: widget tipo di carattere e il documento di carattere.

Ogni font ha diversi aspetti, e il suo allineamento potrebbe essere diverso se si considera questi tipi di carattere come base per il disegno di coordinate.

Dal momento che si sta disegnando con il carattere del documento, ma utilizzando il widget di carattere come riferimento, il risultato è che avrai disegno di problemi:

  • anche con la stessa dimensione in punti diversi tipi di carattere sono disegnati a diverse altezze, soprattutto se l'allineamento del testo rettangolo non è corretto (anche notare che hai usato un incoerente di allineamento, come Qt.AlignRight | Qt.AlignHCenter sempre prendere in considerazione il corretto allineamento di default è superiore allineamento)
  • stai usando il widget font metrics per impostare il testo rettangolo di altezza, che si differenzia dal documento metriche, così potrai limitare l'altezza del disegno.

A differenza di altri widget, editor di testo ricco in Qt hanno due impostazioni del tipo di carattere:

  • il widget del carattere;
  • (predefinito) documento di tipo di carattere, che può essere sostituito da un QTextOption nel documento;

Il documento sarà sempre ereditare il widget tipo di carattere (o carattere dell'applicazione) per impostazione predefinita, e questo accade anche quando si imposta il tipo di carattere per il widget in fase di runtime, e anche per l'applicazione (a meno che un tipo di carattere è stato impostato in modo esplicito per il widget).

Impostazione del tipo di carattere per l'editor è di solito va bene per situazioni semplici, ma bisogna ricordare che i tipi di carattere che si propaga così i bambini widget ereditano il carattere troppo.

D'altra parte, l'impostazione del tipo di carattere predefinito per il documento non si propagano per i bambini, ma, come spiegato in precedenza, possono essere sostituite da applicazioni di carattere, se modificato in fase di esecuzione.

La soluzione più semplice, nel tuo caso, sarebbe quello di impostare il tipo di carattere per il widget con editor al posto del documento. In questo modo sei sicuro che il LineNumberArea (che ne è l'editore del bambino) erediterà lo stesso tipo di carattere. Con questo approccio non è nemmeno necessario impostare il tipo di carattere del pittore, come sarà sempre utilizzare il widget del carattere.

Nel caso In cui si desidera utilizzare un carattere diverso, e ancora mantenere il corretto allineamento, è necessario considerare la posizione della linea di base del carattere utilizzato per il documento, e l'uso che di riferimento per la linea di base del widget tipo di carattere. Per fare questo, è necessario tradurre la posizione di blocco, con la differenza che il ascent() il carattere di due metriche.

2021-11-20 13:08:21

In altre lingue

Questa pagina è in altre lingue

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