QScrollArea 和 QPainter 可能出现渲染问题

Possible rendering issue with QScrollArea and QPainter

我正在尝试使用 PyQt5 创建字符映射可视化工具。使用 fontTools 库,我正在提取给定 ttf 文件中支持的 UNICODE 代码点。然后使用 QPainter.drawText 我在标签上绘制字形。标签存储在 QGridLayout 中,布局存储在 QScrollArea

一切正常,但我尝试滚动时除外。每当我尝试滚动得太快时,绘制的图像就会重叠。看起来像这样。

标签在 window 失去焦点时正确重绘。

这是我到目前为止的 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_())

我不确定是什么原因造成的。感谢您的帮助!

paintEvent()方法给了我们一个属于QPaintEvent的对象,那个对象通过rect()方法提供了一个QRect,即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)