How To Clear Layout in PySide
I’d like to clear and redraw widgets in my custom PySide2 UI.
GOAL
Today’s goal is to remove and delete all children widgets from the layout.
Environment
Windows 10
Python 3.8.7
PySide2 (5.15.2)
Method
I created sample widget with clear-all button.
import sys from PySide2.QtWidgets import * from PySide2.QtCore import Qt, QEventLoop class MyWidget(QWidget): def __init__(self, parent=None): super(MyWidget, self).__init__(parent) self.main_layout = QVBoxLayout() self.setLayout(self.main_layout) self._generateUI() def _generateUI(self): clear_button = QPushButton("Clear All") clear_button.clicked.connect(self._clearall) self.main_layout.addWidget(clear_button) label = QLabel("Label") self.main_layout.addWidget(label) combobox = QComboBox() combobox.addItems(["comboBox1", "comboBox2"]) self.main_layout.addWidget(combobox) buttons_widget = QWidget() buttons_layout = QHBoxLayout() buttons_widget.setLayout(buttons_layout) for i in range(3): button = QPushButton("button"+str(i)) buttons_layout.addWidget(button) self.main_layout.addWidget(buttons_widget) def _clearall(self): # the function to clear widgets pass if __name__ == '__main__': app = QApplication(sys.argv) widget = MyWidget() widget.show() sys.exit(app.exec_())
1. Get all children
To get the list of children in the layout, use count() and itemAt(). Because the itemAt() function returns QLayoutItem not Qwidget, we should get widget by using QLayoutItem.widget() method.
def _clearall(self): children = [] for i in range(self.main_layout.count()): child = self.main_layout.itemAt(i).widget() if child: children.append(child) print(child) # output # <class 'PySide2.QtWidgets.QPushButton'> # <class 'PySide2.QtWidgets.QLabel'> # <class 'PySide2.QtWidgets.QComboBox'> # <class 'PySide2.QtWidgets.QWidget'>
2. Remove each child item
Use deleteLater() to delete widget.
def _clearall(self): children = [] for i in range(self.main_layout.count()): child = self.main_layout.itemAt(i).widget() if child: children.append(child) for child in children: child.deleteLater()
* There are many ways to remove widget from the parent layout. For example, some people use setParent(None) or something. However don’t use removeWidget() because it just removes widget without destroying it. Please refer to the discussion below.
- Is deleteLater() necessary in PyQt/PySide? in stackoverflow.com
- pyqt: how to remove a widget? in stackoverflow.com
Other Examples
There are another methods to implement _clearall() function.
The method in which items deleted in for loop
It works well because the deleteLater() destroys items after returning control from the function.
def _clearall(self): for i in range(self.main_layout.count()): child = self.main_layout.itemAt(i).widget() child.widget().deleteLater()
This is a wrong example. The itemAt() function can’t find the child because when an item is deleted with setParent(None), other items will be renumbered.
def _clearall(self): #wrong example for i in range(self.main_layout.count()): child = self.main_layout.itemAt(i).widget() child.setParent(None) # An error occurred: AttributeError: 'NoneType' object has no attribute 'widget'
The method by using children() or findChildren() function
You can get children of widget with children() function.
def _clearall_4(self): #wrong for child in self.children(): print(child.__class__) child.deleteLater() # output # <class 'PySide2.QtWidgets.QVBoxLayout'> # children() returns layout too # <class 'PySide2.QtWidgets.QPushButton'> # <class 'PySide2.QtWidgets.QLabel'> # <class 'PySide2.QtWidgets.QComboBox'> # <class 'PySide2.QtWidgets.QWidget'>
You can get children of widget with findChild() function.
def _clearall_4(self): #wrong for child in self.findChildren(QWidget): print(child.__class__) child.deleteLater() # output # <class 'PySide2.QtWidgets.QPushButton'> # <class 'PySide2.QtWidgets.QLabel'> # <class 'PySide2.QtWidgets.QComboBox'> # <class 'PySide2.QtWidgets.QWidget'> # <class 'PySide2.QtWidgets.QPushButton'> # <class 'PySide2.QtWidgets.QPushButton'> # <class 'PySide2.QtWidgets.QPushButton'>
This is a wrong example. The children() of layout returns [].
def _clearall_4(self): #wrong example for child in self.main_layout.children(): child.deleteLator() print("first layout children", self.main_layout.children()) # output => []