首页 > 解决方案 > 不应再在小部件的paintEvent 上调用paintEngine

问题描述

我正在尝试在单击时重新绘制一个列表小部件项目,但我在使用QPainter. 代码“有效”,但在每次 GUI 重绘时,它都会显着滞后并输出下面的警告,并且不知道是什么原因造成的,因为它都是在内部完成的paintEvent(也许我没有QPainter正确交换对象?)

每次重绘时,GUI 都会冻结并输出:

QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::fillPath: Painter not active
QPainter::setPen: Painter not active
QPainter::drawPath: Painter not active
QPainter::setClipPath: Painter not active
QPainter::end: Painter not active, aborted

pyqt

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtGui
from PyQt5.QtGui import QColor, QFont, QImage, QPainter, QPainterPath, QPixmap


class QCustomQWidget(QtWidgets.QWidget):
    def __init__(self, parent=None, ref_parent=None):
        super(QCustomQWidget, self).__init__(parent)
        self.textQVBoxLayout = QtWidgets.QVBoxLayout()
        self.ref_parent = ref_parent
        self.shadow_effects = {}
        self.shadow_effects_counter = 0
        self.textUpQLabel = QtWidgets.QLabel()
        font = QFont()
        font.setPointSize(12)
        self.textUpQLabel.setFont(font)
        self.textQVBoxLayout.addWidget(self.textUpQLabel)
        self.allQGrid = QtWidgets.QGridLayout()
        self.thumbnailQLabel = QtWidgets.QLabel()
        self.allQGrid.addWidget(self.thumbnailQLabel, 0, 0, 2, 1, QtCore.Qt.AlignLeft)
        self.allQGrid.addLayout(self.textQVBoxLayout, 0, 1, 2, 1, QtCore.Qt.AlignLeft)
        self.setLayout(self.allQGrid)

    def paintEvent(self, event):
        target = self
        painter = QPainter(target)
        painter = self.set_painter_color(painter, target, QColor(224, 224, 224))
        painter.end()

        if self.ref_parent.item_widget_to_repaint is not None:
            # change color for the clicked list item
            target = self.ref_parent.item_widget_to_repaint
            painter = QPainter(target)
            painter = self.set_painter_color(painter, target, QColor(129, 173, 244))
            painter.end()

    def set_painter_color(self, painter, target, color: QColor):
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

        rect = QtCore.QRectF(target.rect())

        painter_path = QPainterPath()
        painter_path.addRoundedRect(rect, 20, 20)
        painter.fillPath(painter_path, QtGui.QBrush(color))
        painter.setPen(QtCore.Qt.NoPen)  # remove border when clipping
        painter.drawPath(painter_path)
        painter.setClipPath(painter_path)
        return painter

    def setTextUp(self, text):
        self.textUpQLabel.setText(text)
        self.textUpQLabel.setSizePolicy(
            QtWidgets.QSizePolicy(
                QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding
            )
        )


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setLayout(QtWidgets.QVBoxLayout())
        self.checkbox_dict = dict()
        self.button_dict = dict()
        self.listWidget = QtWidgets.QListWidget()
        self.layout().addWidget(self.listWidget)
        self.item_widget_to_repaint = None
        text = ["ITEM 1", "ITEM 2", "ITEM 3"]
        for i in text:
            myQCustomQWidget = QCustomQWidget(ref_parent=self)
            myQCustomQWidget.setTextUp(i)
            myQListWidgetItem = QtWidgets.QListWidgetItem(self.listWidget)
            myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
            self.listWidget.addItem(myQListWidgetItem)
            self.listWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)

        self.listWidget.itemClicked.connect(lambda item: self.on_list_item_click(item))

        self.resize(800, 300)
        self.show()

    def on_list_item_click(self, item: QtWidgets.QListWidgetItem):
        widget = item.listWidget().itemWidget(item)
        # set target for paintevent in QCustomQWidget
        self.item_widget_to_repaint = widget


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec())

标签: pythonqtpyqtpyqt5

解决方案


小部件的绘制应该只在它自己的paintEvent中完成,而不是像你正在做的那样在另一个小部件中完成,这就是Qt用这些错误消息发出的警告。

在这种情况下,逻辑是创建一个包含颜色并调用以重新绘制小部件的属性。

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class QCustomQWidget(QtWidgets.QWidget):
    def __init__(self, parent=None, ref_parent=None):
        super(QCustomQWidget, self).__init__(parent)
        self.textQVBoxLayout = QtWidgets.QVBoxLayout()
        self.ref_parent = ref_parent
        self.shadow_effects = {}
        self.shadow_effects_counter = 0
        self.textUpQLabel = QtWidgets.QLabel()
        font = QtGui.QFont()
        font.setPointSize(12)
        self.textUpQLabel.setFont(font)
        self.textQVBoxLayout.addWidget(self.textUpQLabel)
        self.allQGrid = QtWidgets.QGridLayout(self)
        self.thumbnailQLabel = QtWidgets.QLabel()
        self.allQGrid.addWidget(self.thumbnailQLabel, 0, 0, 2, 1, QtCore.Qt.AlignLeft)
        self.allQGrid.addLayout(self.textQVBoxLayout, 0, 1, 2, 1, QtCore.Qt.AlignLeft)

        self._color = QtGui.QColor(224, 224, 224)

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        if self.color == color:
            return
        self._color = color
        self.update()

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)
        rect = QtCore.QRectF(self.rect())
        painter_path = QtGui.QPainterPath()
        painter_path.addRoundedRect(rect, 20, 20)
        painter.fillPath(painter_path, QtGui.QBrush(self.color))
        painter.setPen(QtCore.Qt.NoPen)
        painter.drawPath(painter_path)
        painter.setClipPath(painter_path)

    def setTextUp(self, text):
        self.textUpQLabel.setText(text)
        self.textUpQLabel.setSizePolicy(
            QtWidgets.QSizePolicy(
                QtWidgets.QSizePolicy.MinimumExpanding,
                QtWidgets.QSizePolicy.MinimumExpanding,
            )
        )


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setLayout(QtWidgets.QVBoxLayout())
        self.checkbox_dict = dict()
        self.button_dict = dict()
        self.listWidget = QtWidgets.QListWidget()
        self.layout().addWidget(self.listWidget)
        self.item_widget_to_repaint = None
        text = ["ITEM 1", "ITEM 2", "ITEM 3"]
        for i in text:
            myQCustomQWidget = QCustomQWidget(ref_parent=self)
            myQCustomQWidget.setTextUp(i)
            myQListWidgetItem = QtWidgets.QListWidgetItem(self.listWidget)
            myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
            self.listWidget.addItem(myQListWidgetItem)
            self.listWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)

        self.listWidget.itemClicked.connect(self.on_list_item_click)

        self.resize(800, 300)
    def on_list_item_click(self, item: QtWidgets.QListWidgetItem):
        widget = item.listWidget().itemWidget(item)
        widget.color = QtGui.QColor(129, 173, 244)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())

推荐阅读