9

I was trying to find something that would take a qt layout and delete everything from it. Just to imagine what the window looks like - I have:

QVBoxLayout
     | ------QHboxLayout
                 |---------QWidget
     | ------QHboxLayout
                 |---------QWidget
            .........

So I need something that I can call recursively to CLEAR AND DELETE all the stuff from my parent QVBoxLayout. I tried things mentioned here (Clear all widgets in a layout in pyqt) but none of them work (no correct answer marked anyway). My code looks like this:

def clearLayout(self, layout):
    for i in range(layout.count()):
        if (type(layout.itemAt(i)) == QtGui.QHBoxLayout):
            print "layout " + str(layout.itemAt(i))
            self.clearLayout(layout.itemAt(i))
        else:
            print "widget" + str(layout.itemAt(i))
            layout.itemAt(i).widget().close()

But it gives an error:

               layout.itemAt(i).widget().close()
            AttributeError: 'NoneType' object has no attribute 'close'

=>EDIT This kinda works (but doesn't if there is any other Layout than HBoxLayout:

def clearLayout(self, layout):
    layouts = []
    for i in range(layout.count()):
        if (type(layout.itemAt(i)) == QtGui.QHBoxLayout):
            print "layout " + str(layout.itemAt(i))
            self.clearLayout(layout.itemAt(i))
            layouts.append(layout.itemAt(i))
        else:
            print "widget" + str(layout.itemAt(i))
            if (type(layout.itemAt(i)) == QtGui.QWidgetItem):
                layout.itemAt(i).widget().close()

2 Answers 2

35

The safest way to clear a layout is to extract the items with its takeAt method, and then explicitly delete any widgets with deleteLater:

def clearLayout(self, layout):
    if isinstance(layout, QLayout):
        while layout.count():
            item = layout.takeAt(0)
            widget = item.widget()
            if widget is not None:
                widget.deleteLater()
            else:
                self.clearLayout(item.layout())

PS: to ensure that everything is deleted immediately, rather than after control returns to the event-loop, see this answer.

Sign up to request clarification or add additional context in comments.

4 Comments

it isn't clear what self is in this context
@pretzlstyle It doesn't really matter what self is. The OP's code uses a method of a class, so that's what my answer uses. It could just as easily be function with no self at all.
thanks for the solution, is all I expected to be a built-in method in pyside6
This worked perfect minus the self.clearLayout(item.layout()) portion. Just had to change that. Thanks!!
7

The problem with your code is QLayout.itemAt() returns a QLayoutItem, QWidgetItem or QSpacerItem depending on the item at that position. So the condition:

type(layout.itemAt(i)) == QtGui.QHBoxLayout

will never be True and you will be trying to do .widget() for a QLayoutItem and that returns None. Thus the error you get. Another thing is, you need to loop backwards. Because removing things from the beginning will shift items and change the order of items.

You need to write your function like this:

def clearLayout(self, layout):
    for i in reversed(range(layout.count())):
        item = layout.itemAt(i)

        if isinstance(item, QtGui.QWidgetItem):
            print "widget" + str(item)
            item.widget().close()
            # or
            # item.widget().setParent(None)
        elif isinstance(item, QtGui.QSpacerItem):
            print "spacer " + str(item)
            # no need to do extra stuff
        else:
            print "layout " + str(item)
            self.clearLayout(item.layout())

        # remove the item from layout
        layout.removeItem(item)    

3 Comments

almost there but not quite :) Thanks anyway :) When I run the function you supplied all my QHboxLayouts get clasified as spacers. Since this is not true " returns a QLayoutItem, QWidgetItem or QSpacerItem depending on the item at that position." => My itemAt returns QHboxLayout which is not QLayoutItem
I sorta fixed it like this: def clearLayout(self, layout): for i in range(layout.count()): if (type(layout.itemAt(i)) == QtGui.QHBoxLayout): print "layout " + str(layout.itemAt(i)) self.clearLayout(layout.itemAt(i)) else: print "widget" + str(layout.itemAt(i)) if (type(layout.itemAt(i)) == QtGui.QWidgetItem): layout.itemAt(i).widget().close()
@user965847: That's weird. I get QLayoutItems for my trials. Anyways, I edited the code. Try this one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.