【发布时间】:2021-04-04 16:09:16
【问题描述】:
我正在尝试在单击时重新绘制列表小部件项目,但我在使用 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
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())
【问题讨论】:
-
你不能“决定”在什么上绘制,因为 UI 的绘制事件仅由 Qt 调用,并且如果绘制事件发生,QPainter 只能在小部件上创建;试图在paintEvent 之外这样做是完全错误的,甚至可能导致崩溃。真正的问题是:为什么需要在这些自定义小部件上调用更新?如果是为了在点击时更新它们,那么实现它们的
mousePressEvent。