Возможная проблема рендеринга с QScrollArea и QPainter

Я пытаюсь создать инструмент визуализации карты символов с помощью PyQt5. С библиотекой fontTools я извлекаю кодовые точки UNICODE, поддерживаемые в данном файле ttf. Затем с помощью QPainter.drawText я рисую глифы на этикетках. Ярлыки хранятся в QGridLayout, а макет в QScrollArea

Все работает нормально, за исключением случаев, когда я пытаюсь прокрутить. Нарисованные изображения перекрываются всякий раз, когда я пытаюсь прокручивать слишком быстро. Похоже на это. шапка экрана

Метки перерисовываются правильно, когда окно теряет фокус.

Вот MWE того, что у меня есть до сих пор.

import sys

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFontDatabase, QFont, QColor, QPainter

from fontTools.ttLib import TTFont

class App(QWidget):
    def __init__(self):
        super().__init__()
        self.fileName = "mukti.ttf" #the ttf file is located in the same path as the script

        self.initUI()

    def initUI(self):
        self.setWindowTitle("Glyph Viewer")
        self.setFixedSize(640, 480)
        self.move(100, 100)

        vBox = QtWidgets.QVBoxLayout()
        self.glyphView = GlyphView()
        vBox.addWidget(self.glyphView)

        self.setLayout(vBox)

        self.showGlyphs()

        self.show()

    def showGlyphs(self):
        #Using the fontTools libray, the unicode blocks are obtained from the ttf file
        font = TTFont(self.fileName)
        charMaps = list()
        for cmap in font['cmap'].tables:
            charMaps.append(cmap.cmap)
        charMap = charMaps[0]

        fontDB = QFontDatabase()
        fontID = fontDB.addApplicationFont("mukti.ttf")
        fonts = fontDB.applicationFontFamilies(fontID)
        qFont = QFont(fonts[0])
        qFont.setPointSize(28)

        self.glyphView.populateGrid(charMap, qFont)

class GlyphView(QtWidgets.QScrollArea):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.setWidgetResizable(True)

    def populateGrid(self, charMap, qFont):
        glyphArea = QtWidgets.QWidget(self)
        gridLayout = QtWidgets.QGridLayout()
        glyphArea.setLayout(gridLayout)

        row, col = 1, 1
        for char in charMap:
            uni = charMap[char]
            gridLayout.addWidget(Glyph(qFont, chr(char)), row, col)
            if not col % 4:
                col = 1
                row += 1
            else:
                col += 1

        self.setWidget(glyphArea)

class Glyph(QtWidgets.QLabel):
    def __init__(self, font, char):
        super().__init__()
        self.font = font
        self.char = char

        self.initUI()

    def initUI(self):
        self.setFixedSize(48, 48)
        self.setToolTip(self.char)

    def paintEvent(self, event):
        qp = QPainter(self)
        qp.setBrush(QColor(0,0,0))
        qp.drawRect(0, 0, 48, 48)
        qp.setFont(self.font)
        qp.setPen(QColor(255, 255, 255))
        qp.drawText(event.rect(), Qt.AlignCenter, self.char)

app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

Я не уверен, что вызывает это. Любая помощь приветствуется!


person ChesterX    schedule 18.10.2018    source источник


Ответы (1)


Метод paintEvent() дает нам объект, принадлежащий QPaintEvent, этот объект предоставляет QRect через метод rect(), QRect — это видимая в данный момент область, и эту информацию можно использовать для оптимизации рисования, например, скажем, у нас есть виджет, который показывает текст в несколько строк, если текст большой, несколько строк будут выглядеть так, что закрашивание всего будет пустой тратой ресурсов, поэтому при правильном расчете с использованием вышеупомянутого QRect мы могли бы получить эти строки и закрасить эту часть, потратив немного ресурсов. В этом ответе я показываю пример использования event.rect().

В вашем случае нет необходимости использовать event.rect(), так как вы будете рисовать текст в части виджета виджета. то, что вы должны использовать, это self.rect():

def paintEvent(self, event):
    qp = QPainter(self)
    qp.setBrush(QColor(0,0,0))
    qp.drawRect(0, 0, 48, 48)
    qp.setFont(self.font())
    qp.setPen(QColor(255, 255, 255))
    # change event.rect() to self.rect()
    qp.drawText(self.rect(), Qt.AlignCenter, self.text())

Я также не вижу необходимости перезаписывать метод paintEvent(), поскольку вы можете указать непосредственно на QLabel шрифт, текст и выравнивание:

class Glyph(QtWidgets.QLabel):
    def __init__(self, font, char):
        super().__init__(font=font, text=char, alignment=Qt.AlignCenter, toolTip=char)
person eyllanesc    schedule 18.10.2018
comment
Спасибо за объяснение! - person ChesterX; 18.10.2018