从布局中移除所有的元素

8

我想找一个可以删除qt布局中所有内容的工具。就像你可以想象出窗口长什么样子一样 - 我有:

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

所以我需要一个可以递归调用的东西,来清除并删除我父容器QVBoxLayout中的所有内容。我尝试了这里提到的东西(在PyQt中清除布局中的所有小部件),但它们都不起作用(无正确答案标记)。我的代码看起来像这样:

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()

但是它会出现错误:
               layout.itemAt(i).widget().close()
            AttributeError: 'NoneType' object has no attribute 'close'

=>编辑 这个有点起作用(但是如果有任何其他的Layout而不是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个回答

30

清除布局最安全的方法是使用其takeAt方法提取项目,然后使用deleteLater明确删除任何小部件:

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

这是一个更优雅的解决方案。谢谢。 - tisaconundrum
在这个上下文中,“self”是什么并不清楚。 - pretzlstyle
@pretzlstyle self是什么并不是很重要。OP的代码使用了一个类的方法,所以我的答案也是用了这个。它同样可以是没有self的函数。 - ekhumoro
谢谢你的解决方案,这正是我期望在 PySide6 中有一个内置方法。 - sebwr

7
你的代码存在问题,QLayout.itemAt() 会返回一个 QLayoutItemQWidgetItemQSpacerItem,具体取决于该位置上的项目类型。因此,这个条件语句如下:
type(layout.itemAt(i)) == QtGui.QHBoxLayout

这段代码中,如果条件判断语句永远为False,你将会尝试使用.widget()用于QLayoutItem并返回None。因此你得到了错误。另一件事是,你需要反向循环。因为从开头删除东西会导致项目移动并且更改项目顺序。

你需要像这样编写你的函数:

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)    

差不多了,但还差一点 :) 无论如何还是谢谢 :) 当我运行您提供的函数时,所有我的QHboxLayouts都被归类为间隔符。由于这不是真实情况,“返回一个QLayoutItem、QWidgetItem或QSpacerItem,取决于该位置上的项。” => 我的itemAt返回的是QHboxLayout而不是QLayoutItem。 - kosta5
我是这样修复它的: 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() - kosta5
@user965847:很奇怪,我的尝试中得到了QLayoutItem。无论如何,我已经编辑了代码,请尝试这个版本。 - Avaris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接