How to remove all widgets and layouts from a QWidget window

Confused about removing widgets from screen…

I know that you can loop through widgets added to layouts and then use the deleteafter or setparent methods to remove them but for some reason when I do this the widgets and layouts remain. Was wondering if it’s easier just to remove the base layout and if so how to ?

self.page_layout is a property in the main class which is a Horizontal Boxlayout I want as the base layout for the window.

for widget_no in range(0, self.page_layout.count()):
  if self.page_layout.itemAt(widget_no) != None:
    if "Layout" not in str(self.page_layout.itemAt(widget_no)):
      self.page_layout.itemAt(widget_no).widget().deleteLater()
    else:
      self.page_layout.itemAt(widget_no).deleteLater()

thanks in advance

To answer my own question.

You need to remove all the widgets from each sublayout first before removing the sublayout. Not doing so leaves the widgets in memory I believe… deleteLater() doesn’t seem to handle this.

Recursive Clear:

def clear_page(self, layout):

  for widget_no in range(0,layout.count()):
    if layout.itemAt(widget_no) != None:
      if "Layout" not in str(layout.itemAt(widget_no)):
        layout.itemAt(widget_no).widget().deleteLater()
      else:
        self.clear_page(layout.itemAt(widget_no))
  
  if layout == self.page_layout():
    self.content_deleted = True
    self.change_page()
1 Like

Hey @weave glad you’ve figured it out. As you say, you do need to handle the deletion all the way down yourself.

If I’m working with custom widgets I usually implement a custom method on the widget which deletes it’s own children, e.g. called teardown or similar. If you nest multiple of these widgets inside one another, you can just recursively call teardown to clear them out.

If you’re working with standard Qt widgets, it’s handy to wrap this into a function which will, when passed a layout or a widget, iterate all the way down.

A warning about deleteLater(): in some situations this can lead to crashes. I suspect this occurs when Qt decides to delete a C++ object before the Python one has been cleared up (garbage collected). From testing recently, you don’t usually need the deleteLater() as long as you take care to eliminate any remaining Python references to the objects. They’ll be dutifully cleaned up, and then the C++ object discarded in an orderly fashion.

Thanks for the reply and suggestions Martin.

Is there any example of a teardown that I can have a look at ? I’m a bit confused what you mean by ensuring you delete the C++ object and python references.

thanks

I think I understand now …

Teardown is unit testing terminology for pyqt6 where you manage memory of objects and references from python. So in my case where I want to be able to clear layouts and their widgets I have to have a method attached to main widget that will take care of this recursively. (added additional on end of loop)

def clear_page(self, layout):
for widget_no in range(0, layout.count()):
if “Layout” not in str(layout.itemAt(widget_no)):
layout.itemAt(widget_no).widget().deleteLater() # assumes widget is not referenced
else:
self.clear_page(layout.itemAt(widget_no))

if layout == self.child_layout: # referenced form base layout remains
self.child_detail_layout = QHBoxLayout()
self.child_detail_col0_layout = QVBoxLayout()
self.child_detail_col1_layout = QVBoxLayout()
else: # sublayouts referenced cleanup
if layout == self.child_detail_layout:
layout.deleteLater()
self.child_detail_layout = None
elif layout == self.child_detail_col0_layout:
layout.deleteLater()
self.child_detail_col0_layout = None
elif layout == self.child_detail_col1_layout:
layout.deleteLater()
self.child_detail_col1_layout = None