Problem with Custom Shelf Tool Python Window

   7016   5   4
User Avatar
Member
183 posts
Joined: Dec. 2011
Offline
I am trying to write a python window that launches when a shelf tool button is pressed. I have been able to launch the window, however it seems that all the references to my widgets break once the window is initialized. Callbacks and anything that happens afterwards result in a “RuntimeError: Internal C++ object (PySide.QtGui.QListWidget) already deleted.” I'm guessing that this has something to do with Houdini's event loop?

I will try to post some example code tomorrow, but I just wanted to post this and see if anyone had any ideas.
User Avatar
Member
183 posts
Joined: Dec. 2011
Offline
So here is the sample code I promised. After further testing it, the problem definitely has to do with parenting the window to Houdini's mainQtWindow. The .ui file that the code is reading in is just a button and a qlineedit inside a horizontal layout. This script is being run via a shelf tool.

import os
from PySide import QtCore
from PySide import QtGui
from PySide import QtUiTools

class TweakedDemo(QtGui.QWidget):
    def __init__(self, parent=None):
        super(TweakedDemo, self).__init__(parent)

        loader = QtUiTools.QUiLoader()
        gui_path = os.path.join(os.getenv('HIP'),'testGui.ui')
        self.gui = loader.load(gui_path)

        self.setGeometry(500, 300, 250, 110)
        self.setWindowTitle('Tweaked Demo')

        button = self.gui.findChild(QtGui.QPushButton, "button")
        self.field = self.gui.findChild(QtGui.QLineEdit, "textField")
        
        self.connect(button, QtCore.SIGNAL('clicked()'), self.changeText)

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(self.gui)
        self.setLayout(vbox)

    def changeText(self):
        self.field.setText('Hello World')
            
    def closeEvent(self, event):
        self.setParent(None)

dialog = TweakedDemo()
dialog.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
dialog.show()

Just to reiterate, if I click the button on this example I get a
RuntimeError: Internal C++ object (PySide.QtGui.QListWidget) already deleted.
error. Which seems odd since the whole point of parenting the window to Houdini is to avoid references being deleted.

If I get rid of the
dialog.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
line then it runs, but only if the class is defined inside the tool's script window. If I move the class into an external file and then load it as a module, then the window is deleted as soon as it's made. My guess is that this is due to its references being cleaned up since it is not parented to Houdini's Qt session.

Is there a way to work around this?
Edited by NFX - March 29, 2018 13:44:40
User Avatar
Staff
1255 posts
Joined: July 2005
Offline
Hello,

I think I have run into this issue before. It has to do with the lifespan of Python variables and PySide/shiboken deciding what it owns and what it can delete. It's a long explanation but here are a couple of workarounds you can try.

1) Assign the result of hou.ui.mainQtWindow()to a variable that lives past the lifetime of the shelf tool's Python context. For example:
hou.session.mainWindow = hou.ui.mainQtWindow()
dialog.setParent(hou.session.mainWindow, QtCore.Qt.Windows)

2) Alternatively, don't parent to the main window. Instead assign your dialog to a variable that lives past the lifetime of the shelf tool's Python context. For example:
hou.session.dialog = dialog

I hope this helps.

Cheers,
Rob
User Avatar
Member
183 posts
Joined: Dec. 2011
Offline
Both of these solutions work like a charm. I didn't know you could dynamically declare variables in hou.session like this (though in retrospect this totally makes sense). I'll have to remember that trick for the future.
User Avatar
Member
20 posts
Joined: March 2019
Offline
Thanks Rob!

If anyone can explain why storing the hou.ui.mainQtWindow() in a variable fixes the issue I'd really like to know. Is this just a bug, or is there logic behind why the c++ object gets prematurely deleted?
User Avatar
Member
359 posts
Joined: April 2017
Offline
It has to do with garbage collection in Python. If you create an instance of an object but there's nothing in the main loop that's actually referencing that instance, Python will automatically purge it from memory. The moment you either connect the window to something permanent like the MainWindow, or store it in a persistent object like hou.session, Python recognizes that something else in the event loop is storing a pointer of sorts to this new window, and so it won't be removed.
MOPs (Motion Operators for Houdini): http://www.motionoperators.com [www.motionoperators.com]
  • Quick Links