PySide: Removing a widget from a layout

Etienne Perot picture Etienne Perot · Mar 28, 2012 · Viewed 14.5k times · Source

I'm trying to remove a Qt widget from a layout in a PySide application.

Here is a minimal example. It is a widget with 5 buttons in it, and the middle one is supposed to remove itself when clicked:

import sys
from PySide import QtGui

app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
buttons = [QtGui.QPushButton(str(x)) for x in xrange(5)]

def deleteButton():
    b = layout.takeAt(2)
    buttons.pop(2)
    del b
buttons[2].clicked.connect(deleteButton)

map(layout.addWidget, buttons)
widget.setLayout(layout)
widget.show()
app.exec_()

What actually happens is this:

What actually happens

The button is unclickable and clearly isn't taken into consideration for the layout computations, but its image stays in place.

According to the Qt documentation, the correct way of deleting all objects from a layout is:

while ((child = layout->takeAt(0)) != 0) {
    delete child;
}

Here I just want to delete the third button, so I just call takeAt(2), and then del b to call the destructor on that item. The button object is also .pop'd from the buttons list to make sure there is no leftover reference to the object. How does my code differ from the one in the Qt docs that would cause such a behavior?

Answer

jdi picture jdi · Mar 28, 2012

Super simple fix:

def deleteButton():
    b = layout.takeAt(2)
    buttons.pop(2)
    b.widget().deleteLater()

You first have to make sure you are addressing the actual button and not the QWidgetItem that is returned from the layout, and then call deleteLater() which will tell Qt to destroy the widget after this slot ends and control returns to the event loop.

Another example illustrates why the problem is occurring. Even though you take the layout item, the underlying widget is still parented to the original layouts widget.

def deleteButton():
    b = layout.takeAt(2)
    buttons.pop(2)
    w = b.widget()
    w.setParent(None)

This is not the preferred way, as it still leaves the cleanup of the object ambiguous. But it shows that clearing the parent allows it to leave the visual display. Use deleteLater() though. It properly cleans everything up.