[PyQt] itemChange vs reparent vs pyqtSlot

oliver oliver.schoenborn at gmail.com
Mon Aug 15 21:13:42 BST 2016


We need to reparent a graphics item to another. My colleague came across
the weirdest bug: just decorating a function with pyqtSlot(QPushButton)
causes QGraphicsItem.itemChange() to fail reparenting (reparentItem()).
Just run the following example with stock PyQt 5.5.1 (downloaded from
riverbank website in binary form) and observe the output:

import sys
from PyQt5.QtCore import pyqtSlot, QVariant, QObject
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene,
QPushButton, QGraphicsItem, QGraphicsRectItem


# no decorator:              Good
# @pyqtSlot(QObject)       # Good
# @pyqtSlot(QWidget)       # Good
# @pyqtSlot(list)          # Good
# @pyqtSlot(int)           # Good
# @pyqtSlot(QGraphicsItem) # reparenting BROKEN!!!
@pyqtSlot(QPushButton)     # reparenting BROKEN!!!
def callback(button: QPushButton):
    pass


class Test(QGraphicsRectItem):
    def __init__(self, parent):
        super().__init__(parent)

    def itemChange(self, change: QGraphicsItem.GraphicsItemChange,
new_value: QVariant) -> QVariant:
        return super(Test, self).itemChange(change, new_value)

    def type(self) -> int:
        return QGraphicsItem.UserType + 100


app = QApplication(sys.argv)
scene = QGraphicsScene()
view = QGraphicsView(scene)
view.show()

parent1 = QGraphicsRectItem()
parent2 = QGraphicsRectItem()
scene.addItem(parent1)
scene.addItem(parent2)

test = Test(parent1)

print("Old parent:", test.parentItem())
test.setParentItem(parent2)
print("New parent:", test.parentItem())

sys.exit(app.exec_())



The output shows that the reparenting failed (shows None). With suitable
print statements, you can see that Test.itemChange(ParentChange, value) is
called, but the return of super() itemChange() is None (which is like
making the object refuse to reparent itself, except that the original
parentage is not restored). The setParentItem() returns None. In our actual
app, the reparented item appears in the scene away from the intended
parent, and does not move with parent.

Now comment out the @pyqtSlot decoration, reparenting works. Or, use a
different type of arg for pyqtSlot like int or list (or even QWidget),
works. The setParentItem() returns parent. There are other types that cause
failure too (like QGraphicsItem) when used in the decorator.

We tried the sip.cast() mentioned in
https://riverbankcomputing.com/pipermail/pyqt/2012-August/031819.html, but
that did not help (the super().itemChange() still returns None). My
colleague posted to stackoverflow
<http://stackoverflow.com/questions/38925044/parentitem-returns-none-not-expected>
with
similar script but I'm posting for him here since he is not on this mailing
list!

If anyone can try this in PyQt 5.7 and let us know if the bug is gone, that
would be good enough since upgrading to that version is on our short-term
roadmap.

If the bug is still in latest 5.7 binary release, I'm really hoping someone
can suggest a workaround that would allow us to continue to use itemChange,
setParentItem and QPushButton or QGraphicsItem as arg to pyqtSlot decorator.

Best.
Oliver
Open Source contributions: PyPubSub <http://pubsub.sf.net/>, nose2pytest
<https://github.com/schollii/nose2pytest>, Lua-iCxx
<http://lua-icxx.sf.net/>, iof <http://iof.sf.net/>
StackOverflow <http://stackoverflow.com/users/869951/schollii> contributions
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20160815/f2c11807/attachment.html>


More information about the PyQt mailing list