清空QLayout中所有控件的方法
前言
在 GUI 开发中,当我们使用 PySide6(或兼容的PyQt6)的 QVBoxLayout 或 QHBoxLayout 布局管理器来组织界面元素,包括控件、弹簧以及其他嵌套布局时,用户界面的动态性要求我们能够在用户操作后清空布局内的所有内容(如下图所示),并重新填充新的元素。
由于 QLayout 类没有内置一键清空所有子项的功能,必须采取一种手动的方法来实现这一需求。在本文中,将深入探讨并展示如何有效地清空 QLayout(无论是垂直布局还是水平布局)中的所有控件、子布局以及弹簧,为界面的重新布局和元素更新奠定坚实的基础。
正文
1、takeAt()方法
QLayout 类不仅没有一键清空所有子项的 clear() 方法,也没有移除 remove() 方法,经过在网上查阅的资料,大家普遍使用的是 takeAt() 结合 deleteLater() 的方法:
- takeAt():用于从布局中移除并返回指定索引处的项(QLayoutItem),通常用于在迭代过程中动态地修改布局,比如移除布局中的某些组件。
- deleteLater():安排一个对象在其事件循环的适当时间被删除,通常用于确保在对象不再需要时正确地释放资源,同时避免在删除过程中发生潜在的线程冲突或其他问题。
大致的用法如下:
# 获取布局中控件的数量
while layout.count():
# 从布局中移除第一个控件
item = layout.takeAt(0)
widget = item.widget()
if widget:
widget.deleteLater()
在实际使用时发现,使用这种方法,当再次添加时的控件布局与之前的布局一致时,不会有问题;可是当添加的布局比前一布局多时,发现多出的部分控件虽然会被删除(即通过layout.count()获取的数量确实为0),但 GUI 界面依然会显示
这里可能的原因是因为:QLayout 的操作都是通过 QLayoutItem 来实现的,QLayoutItem 可能包含 QWidget,也可能不包含 QWidget(如 QLayout、QSpacerItem 等),所以在使用 itemAt(i).widget().deleteLater() 删除前需要进行判断,否则可能会导致之前创建的 QWidget 残留在UI界面上。(个人分析,有不同意见或想法的读者欢迎在评论区留言)
2、自定义f_clearLayoutFunc()方法
所以后续的想法是:判断布局中移除的项是否是布局,如果不是布局,直接移除;如果是布局,则对布局中的控件再进行递归删除。
完整的代码如下:
# 布局清空函数
def f_clearLayoutFunc(self, layout):
"""
function: 布局清空函数
in: layout:要清空的布局;
out: None
return: None
others: Layout Clear Func
"""
if layout is not None:
while layout.count():
# 从布局中移除第一个项
# 注意:removeAt(0) 移除第一个项并返回它,但这里我们不需要返回项
item = layout.takeAt(0)
# 如果这个项是一个布局,我们也需要递归地清除它
widget = item.widget()
if widget is not None:
# 移除控件的父布局,这也会删除控件(如果它是顶级窗口中的控件)
# widget.setParent(None)
widget.deleteLater()
else:
# 如果这个项不是一个控件(可能是一个布局或弹簧),我们也需要清除它
# 注意:对于嵌套的布局,这里会递归地调用clear_layout
self.f_clearFrameFunc(item.layout())
3、setParent(None)方法
这里额外介绍下 setParent(None) 方法:
-
setParent(None) :在 PyQt 中,用于将一个对象从其父对象中分离出来。当一个对象被设置为没有父对象时,它变成了顶级窗口(如果它是一个窗口或窗口部件)或者一个自由浮动的对象(如果它不是窗口),这个操作不会立即删除对象,只是改变了它的父子关系。
如果将一个窗口部件的父对象设置为 None,而这个窗口部件之前是一个非顶级窗口部件(即它有父窗口部件),那么这个窗口部件将不再显示(因为它不再是任何可见窗口部件的一部分),但它仍然存在于内存中,直到显式地删除它(比如通过调用它的 deleteLater() 方法)或者 Python 的垃圾回收机制回收它。
setParent(None) 与 deleteLater() 区别:
- deleteLater() :请求 Qt 在事件循环的下一个迭代中删除对象及其所有子对象,释放相关资源。
- setParent(None) :将对象从其父对象中分离出来,但不立即删除它。如果对象是一个窗口部件,它可能不再可见,但仍然存在于内存中,直到被显式删除或垃圾回收。
在大多数情况下,如果想要删除一个对象及其子对象,应该使用 deleteLater()。如果只是想改变一个对象的父子关系(例如,将一个窗口部件从一个窗口移动到另一个窗口),则应该使用 setParent()。