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:
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?
Show() and hide() widget Each Qt widget has a . setVisible method which can be used to toggle that widget's visibility. >> However, compound or nested widgets can only become invisible when all their child widgets are >>also invisible.
In Qt (and most User Interfaces) 'widget' is the name given to a component of the UI that the user can interact with. User interfaces are made up of multiple widgets, arranged within the window.
Deleting a Layout Element. To delete a layout element from an attribute layout, right-click the actions menu for the layout element information pane and select Delete Group. Inherited layout elements cannot be deleted, but new layout elements added to an inherited layout can be deleted.
We can delete widgets from the window or frame using the . destroy method in tkinter. It can be invoked in the widget by defining a function for it.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With