简体   繁体   English

QScrollArea和QPainter可能的渲染问题

[英]Possible rendering issue with QScrollArea and QPainter

I'm trying to create a character map visualization tool with PyQt5. 我正在尝试使用PyQt5创建字符映射可视化工具。 With fontTools library, I'm extracting the UNICODE code points supported in a given ttf file. 使用fontTools库,我可以提取给定ttf文件中支持的UNICODE代码点。 Then using QPainter.drawText I'm drawing the glyphs on labels. 然后使用QPainter.drawText在标签上绘制字形。 The labels are stored in a QGridLayout and the layout is in a QScrollArea 标签存储在QGridLayout ,布局存储在QScrollArea

Everything works fine, except when I try to scroll. 一切正常,除非我尝试滚动。 The drawn images are overlapped whenever I try to scroll too fast. 每当我尝试太快滚动时,绘制的图像就会重叠。 It looks like this. 看起来像这样。 屏幕盖

The labels are redrawn properly the moment the window loses focus. 窗口失去焦点后,标签会正确重新绘制。

Here's an MWE of what I've so far. 这是我到目前为止的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_())

I'm not sure what is causing this. 我不确定是什么原因造成的。 Any help is appreciated! 任何帮助表示赞赏!

The paintEvent() method gives us an object that belongs to QPaintEvent , that object provides a QRect through the rect() method, that QRect is the area that is currently visible, and that information could be used to optimize the painting, for example let's say we have a widget that shows texts in several lines, if the text is large few lines will look so painting all is a waste of resources, so with a proper calculation using the aforementioned QRect we could get those lines and paint that part spending few resources. paintEvent()方法为我们提供了一个属于QPaintEvent的对象,该对象通过rect()方法提供了一个QRect ,该QRect是当前可见的区域,该信息可用于优化绘画,例如让我们假设我们有一个小部件,它以多行显示文本,如果文本很大,则几行看起来会很浪费,因此绘画全部都是资源的浪费,因此,使用上述QRect进行适当的计算,我们可以得到这些行并仅花很少的QRect绘制该部分资源。 In this answer I show an example of the use of event.rect(). 这个答案中,我展示了一个使用event.rect()的示例。

In your case there is no need to use event.rect() since you would be painting the text in a part of the widget widget. 在您的情况下,无需使用event.rect()因为您将在小部件小部件的一部分中绘制文本。 what you should use is self.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())

I also see unnecessary to overwrite paintEvent() method since you can point directly to the QLabel the font, the text and the alignment: 我还认为不需要覆盖paintEvent()方法,因为您可以直接指向QLabel的字体,文本和对齐方式:

class Glyph(QtWidgets.QLabel):
    def __init__(self, font, char):
        super().__init__(font=font, text=char, alignment=Qt.AlignCenter, toolTip=char)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM