[PyQt] QIconEngine ownership regression in PyQt5 5.12
Ales Erjavec
ales.erjavec324 at gmail.com
Thu Mar 14 13:30:24 GMT 2019
Hi,
There seems to be a regression in PyQt5 5.12 in QIconEngine ownership
transfer to QIcon.
I.e.
### code
icon1 = QIcon(IconEngine(...))
icon2 = QIcon(icon1)
del icon1 # the IconEngine is deleted along with the icon1
### /code
After this icon2 no longer renders properly. The QIconEngine's are
implicitly shared by QIcon
instances so the engine should not be deleted with icon1.
This works as expected in in PyQt5 5.11, 5.10, 5.9
A more complete example with an icon engine implementation:
### code
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import (
QIcon, QIconEngine, QPainter, QColor, QPixmap, QStandardItem,
QStandardItemModel
)
from PyQt5.QtWidgets import QApplication, QListView
class IconEngine(QIconEngine):
"""A simple QIconEngine drawing a single text character."""
def __init__(self, char, color):
# type: (str, QColor) -> None
super().__init__()
self.char = char
self.color = QColor(color)
def paint(self, painter, rect, mode, state):
# type: (QPainter, QRect, QIcon.Mode, QIcon.State) -> None
size = rect.size()
if size.isNull():
return
dpr = painter.device().devicePixelRatioF()
size = size * dpr
painter.drawPixmap(rect, self.pixmap(size, mode, state))
def pixmap(self, size, mode, state):
# type: (QSize, QIcon.Mode, QIcon.State) -> QPixmap
pm = QPixmap(size)
pm.fill(Qt.transparent)
painter = QPainter(pm)
painter.setRenderHints(QPainter.TextAntialiasing)
size = size.width()
painter.setPen(self.color)
painter.setBrush(self.color)
margin = 1 + size // 16
text_margin = size // 20
rect = QRect(margin, margin, size - 2 * margin, size - 2 * margin)
font = painter.font()
font.setPixelSize(size - 2 * margin - 2 * text_margin)
font.setBold(True)
painter.setFont(font)
painter.drawText(rect, Qt.AlignCenter, self.char)
painter.end()
return pm
def clone(self):
return IconEngine(self.char, self.color)
def __del__(self):
print("del")
def main(argv=[]):
app = QApplication(argv)
view = QListView()
model = QStandardItemModel()
item1 = QStandardItem("R")
item1.setIcon(QIcon(IconEngine('R', QColor("red"))))
item2 = QStandardItem("B")
item2.setIcon(QIcon(IconEngine('B', QColor("blue"))))
item3 = QStandardItem("G")
item3.setIcon(QIcon(IconEngine('G', QColor("green"))))
model.appendRow([item1])
model.appendRow([item2])
model.appendRow([item3])
view.setModel(model)
view.show()
return app.exec_()
if __name__ == "__main__":
import sys
sys.exit(main())
### /code
In PyQt5 5.12 the __del__ method is called immediately after setting
the icon on the QStandardItem (and the icons do not render in the
view)
In PyQt5 5.11, 5.10, 5.9 it is called after main finishes (and icons
render correctly in the view).
Tested on macOS 10.14.5 with Python 3.6.6 and 3.7.0
Best regards
Aleš
More information about the PyQt
mailing list