Python/Qt garbage collection

62 views
Skip to first unread message

vince touache

unread,
Oct 15, 2020, 9:13:42 AM10/15/20
to Python Programming for Autodesk Maya
hello, 

I have this good old internal C++ object already deleted and can't understand where it comes from
To summarize, I have a QMainWindow with a QScrollArea, and I want to parent/unparent widgets into this QScrollArea. Ofc I make sure to re-parent the widget I'm about to remove, to make sure it never gets unparented and garbage collected, but it doesn't change anything....

Since I still don't know how to format code on google groups (yay \o/), I updated some code on github if someone wants to try it , and here is a snippet to reproduce the error
 
import main;import foo
m = main.Main() # this is my main window
m.show() 
# now I create 2 widgets, and I want to display the first one in my scrollArea, then the second, then come back on my first
bar1 = foo.Bar("bar1")
bar2 = foo.Bar("bar2")

# now I parent it to my scrollArea
m.something_triggered(bar1)
m.something_triggered(bar2) # this is where bar1 gets deleted, even though i reparent it before replacing with bar2
>>> # RuntimeError: Internal C++ object (PySide2.QtWidgets.QLabel) already deleted. # 

Can anyone explain me why / how my bar1 gets deleted, even though I parent it to my mainWindow to prevent garbage collection?

Thank you

vince touache

unread,
Oct 15, 2020, 12:49:55 PM10/15/20
to Python Programming for Autodesk Maya
it looks like QSCrollArea.setWidget() does a lot more than what it seems. And seems to be partially related to my issue. E.g.

If the scroll area is visible when the widget is added, you must show() it explicitly.

or

Note that You must add the layout of widget before you call this function; if you add it later, the widget will not be visible - regardless of when you show() the scroll area. In this case, you can also not show() the widget later.


As a temp solution, I use a QStackedWidget() to keep all my widgets and display them when needed, but I would really like to understand why my widget is destroyed despite the reparenting, in the example above

cheers

Justin Israel

unread,
Oct 15, 2020, 1:21:32 PM10/15/20
to python_in...@googlegroups.com
I wasn't able to reproduce your problem from the code you provided. You may need to consolidate it into a single file that can be executed, or be more specific about the conditions under which you are executing it. But either way, I agree with your idea that it is related to calling QScrollArea.setWidget. It looked to me that the problem was trying to set the parent on the previous widget before you replace it with another one. My expectations of what setWidget should do would end up still doing something with the previous widget even though you changed its parent. You can actually see exactly what it is doing here: https://github.com/qt/qtbase/blob/dev/src/widgets/widgets/qscrollarea.cpp#L255
It looks like setWidget will always delete the previous one if it is still set, because the QScrollArea takes ownership of it when you first set it. But you can explicitly take the ownership back instead of having the QScrollArea manage the lifetime: https://doc.qt.io/qt-5/qscrollarea.html#takeWidget
So maybe this is the fix:
prev_widget = self.scrollArea.takeWidget()

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/a6fef101-6378-4b3d-9bac-00c95936afd3n%40googlegroups.com.

vince touache

unread,
Oct 15, 2020, 2:12:57 PM10/15/20
to python_in...@googlegroups.com
oh that's weird. Did you try it via mayapy or directly via your python interpreter? I'll give it a try with python/qt outside of maya
anyway, your github link is pure gold, I can indeed see that the scrollArea apparently removed the widget ! I used reparenting a lot in the past, and it always worked well, but my setups were always pretty simple. Here, regardless of the (new) parent, setWidget will remove the previous one first.
I'll give it another try, but this time I'll call QScrollArea.takeWidget() first, store the widget, and only then, call setWidget

I'll let you know how it goes =]

Thank you!

vince touache

unread,
Oct 15, 2020, 2:22:13 PM10/15/20
to python_in...@googlegroups.com
neat, works perfectly! 

w = self.nodesArea.takeWidget()
if w: # just in case the scrollArea was empty
    w.setParent(self)
self.nodesArea.setWidget(node.node.gui)

much more elegant and simple than what I had, thank you so much Justin!

Justin Israel

unread,
Oct 15, 2020, 2:23:28 PM10/15/20
to python_in...@googlegroups.com
On Fri, Oct 16, 2020 at 7:12 AM vince touache <fruit...@gmail.com> wrote:
oh that's weird. Did you try it via mayapy or directly via your python interpreter? I'll give it a try with python/qt outside of maya

Yea I was running it from just a normal interpreter, since I was using a laptop that does not have Maya
 
anyway, your github link is pure gold, I can indeed see that the scrollArea apparently removed the widget ! I used reparenting a lot in the past, and it always worked well, but my setups were always pretty simple. Here, regardless of the (new) parent, setWidget will remove the previous one first.

In general it could have mixed results to rely only on reparenting (as you have now discovered). Qt expects you to observe the documented behaviour of each widget, where they sometimes will tell you that one widget explicitly takes ownership of another. When that is the case, you can't really rely on managing changes to the child widget outside of the new owner, since it is the responsibility of that new owner to release the resource. That allows you to do things like leaving it parented and kicking it over to another widget and not worrying about when to delete it. But it also means you have to tell the owner that you want to take it back again :-)
 

Justin Israel

unread,
Oct 15, 2020, 2:24:17 PM10/15/20
to python_in...@googlegroups.com
On Fri, Oct 16, 2020 at 7:22 AM vince touache <fruit...@gmail.com> wrote:
neat, works perfectly! 

w = self.nodesArea.takeWidget()
if w: # just in case the scrollArea was empty
    w.setParent(self)
self.nodesArea.setWidget(node.node.gui)

much more elegant and simple than what I had, thank you so much Justin!

Nice!
 

vince touache

unread,
Oct 15, 2020, 2:42:10 PM10/15/20
to Python Programming for Autodesk Maya
After almost a decade using Qt (I remember starting on mac os with an article found on your blog Justin, about how to compile qt for maya/mac! ), I am still annoyed by the same things: sometimes, a super common operation (such as drag/drop) has to bee implemented manually, while very simple operations have presets or widgets already done. And from a user perspective, it is really hard to know what exists already and what you should implement yourself. I didn't know how robust it'd be to juggle with parentings on the fly, but since it was not erroring and giving me the result I wanted, I assumed it was fine!
Anyway, super happy to have found exactly the solution I wanted and to understand qt better today than yesterday^^
cheers
Reply all
Reply to author
Forward
0 new messages