清除pyqt中布局中的所有小部件

问题描述 投票:0回答:15

有没有办法清除(删除)布局中的所有小部件?

self.plot_layout = QtGui.QGridLayout()
self.plot_layout.setGeometry(QtCore.QRect(200,200,200,200))
self.root_layout.addLayout(self.plot_layout)
self.plot_layout.addWidget(MyWidget())

现在我想用新的小部件替换

plot_layout
中的小部件。有没有一种简单的方法可以清除
plot_layout
中的所有小部件?我没有看到任何这样的方法。

python qt pyqt pyqt4
15个回答
136
投票

经过大量研究(这花了相当长的时间,所以我将其添加到此处以供将来参考),这是我发现真正清除和删除布局中的小部件的方法:

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().setParent(None)

文档中关于 QWidget 的说法是:

当删除其父级小部件时,新小部件也会被删除。

重要提示:您需要向后循环,因为从开头删除内容会移动项目并更改布局中项目的顺序。

测试并确认布局为空:

for i in range(layout.count()): print i

似乎还有另一种方法可以做到。不要使用 setParent 函数,而是使用 deleteLater() 函数,如下所示:

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().deleteLater()

文档说 QObject.deleteLater (self)

安排删除该对象。

但是,如果运行上面指定的测试代码,它会打印一些值。这表明布局仍然有项目,与带有 setParent 的代码相反。


42
投票

这可能有点太晚了,但只是想添加这个以供将来参考:

def clearLayout(layout):
  while layout.count():
    child = layout.takeAt(0)
    if child.widget():
      child.widget().deleteLater()

改编自 Qt 文档 http://doc.qt.io/qt-5/qlayout.html#takeAt。请记住,当您在 while 或 for 循环中从布局中删除子项时,您实际上是在修改布局中每个子项的索引号。这就是为什么使用

for i in range()
循环会遇到问题。


30
投票

如果您不需要将新的小部件添加到您的布局中,PALEN 的答案很有效。

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().setParent(None)

但是,如果您多次清空并填充布局或使用许多小部件,则在某些时候您会收到“分段错误(核心转储)”。布局似乎保留了一个小部件列表,并且该列表的大小受到限制。

如果您以这种方式删除小部件:

for i in reversed(range(layout.count())): 
    widgetToRemove = layout.itemAt(i).widget()
    # remove it from the layout list
    layout.removeWidget(widgetToRemove)
    # remove it from the gui
    widgetToRemove.setParent(None)

你不会遇到这个问题。


21
投票

这就是我清理布局的方法:

def clearLayout(layout):
    if layout is not None:
        while layout.count():
            child = layout.takeAt(0)
            if child.widget() is not None:
                child.widget().deleteLater()
            elif child.layout() is not None:
                clearLayout(child.layout())

14
投票

可以使用

close()
widget
方法:

for i in range(layout.count()): layout.itemAt(i).widget().close()

7
投票

我用:

    while layout.count() > 0: 
        layout.itemAt(0).setParent(None)

5
投票

大多数现有答案都不考虑嵌套布局,因此我创建了一个递归函数,给定一个布局,它将递归删除其中的所有内容以及其中的所有布局。这是:

def clearLayout(layout):
    print("-- -- input layout: "+str(layout))
    for i in reversed(range(layout.count())):
        layoutItem = layout.itemAt(i)
        if layoutItem.widget() is not None:
            widgetToRemove = layoutItem.widget()
            print("found widget: " + str(widgetToRemove))
            widgetToRemove.setParent(None)
            layout.removeWidget(widgetToRemove)
        elif layoutItem.spacerItem() is not None:
            print("found spacer: " + str(layoutItem.spacerItem()))
        else:
            layoutToRemove = layout.itemAt(i)
            print("-- found Layout: "+str(layoutToRemove))
            clearLayout(layoutToRemove)

我可能没有考虑所有 UI 类型,不确定。希望这有帮助!


3
投票

我解决这个问题的方法是重写QWidget的setLayout方法。以下代码将布局更新为新布局,该布局可能包含也可能不包含已显示的项目。您可以简单地创建一个新的布局对象,向其中添加您想要的任何内容,然后调用 setLayout。当然,你也可以只调用clearLayout来删除所有内容。

def setLayout(self, layout):
    self.clearLayout()
    QWidget.setLayout(self, layout)

def clearLayout(self):
    if self.layout() is not None:
        old_layout = self.layout()
        for i in reversed(range(old_layout.count())):
            old_layout.itemAt(i).widget().setParent(None)
        import sip
        sip.delete(old_layout)

2
投票

来自文档:

要从布局中删除小部件,请调用

removeWidget()
。在小部件上调用
QWidget.hide()
也会有效地从布局中删除小部件,直到调用
QWidget.show()

removeWidget
继承自
QLayout
,这就是为什么它没有列在
QGridLayout
方法中。


2
投票

我对前面提到的解决方案有疑问。有一些挥之不去的小部件导致了问题;我怀疑已计划删除,但尚未完成。我还必须将小部件父级设置为“无”。 这是我的解决方案:

def clearLayout(layout):
    while layout.count():
        child = layout.takeAt(0)
        childWidget = child.widget()
        if childWidget:
            childWidget.setParent(None)
            childWidget.deleteLater()

1
投票

有几个解决方案,如果您使用堆叠小部件在已知视图之间交换,并且仅翻转显示的索引可能比在布局中添加和删除单个小部件容易得多。

如果您想替换 all 某个小部件的子级,那么

QObject
函数
findChildren
应该可以帮助您实现,例如我不知道模板函数是如何包装在 pyqt 中的。但如果您知道这些小部件,您也可以按名称搜索它们。


1
投票
for i in reversed(range(layout.count())):
    if layout.itemAt(i).widget():
        layout.itemAt(i).widget().setParent(None)
    else:
        layout.removeItem(layout.itemAt(i))

0
投票
        for i in reversed (range(layout.count())):
            layout.itemAt(i).widget().close()
            layout.takeAt(i)

        for i in range(layout.count()):
            layout.itemAt(0).widget().close()
            layout.takeAt(0)

0
投票

如果您有嵌套布局但您试图仅删除小部件,则会出现问题。 如果您检查项目类型,并且如果该项目是布局,您应该递归调用该函数,则可以解决这个问题:

def clearLayout(layout):
    for i in reversed(range(layout.count())): 
        child = layout.itemAt(i)
        if child.widget() is not None:
            child.widget().deleteLater()
        elif child.layout() is not None:
            clearLayout(child.layout())

0
投票
class AnyClass(QWidget):
    def __init__(self):
        super().__init__()

        self.layout = QVBoxLayout()
    
        ......
    
        self.setLayout(self.layout)
    
        ......
    
    def clear_layout(self)
        while self.layout.count() > 0:
            item = self.layout.itemAt(0)
            widget = item.widget()
            if widget is None:
                self.layout.removeItem(item)
            else:
                self.layout.removeWidget(widget)
        ......
© www.soinside.com 2019 - 2024. All rights reserved.