Qt - 从布局中删除所有小部件?
这看起来并不容易。基本上,我通过函数将 QPushButton 添加到布局中,当函数执行时,我想首先清除布局(删除所有 QPushButton 以及其中的其他内容)那里),因为更多按钮会附加到滚动视图中。
标头
QVBoxLayout* _layout;
cpp
void MainWindow::removeButtonsThenAddMore(const QString &item) {
//remove buttons/widgets
QVBoxLayout* _layout = new QVBoxLayout(this);
QPushButton button = new QPushButton(item);
_layout->addWidget(button);
QPushButton button = new QPushButton("button");
_layout->addWidget(button);
QWidget* widget = new QWidget();
widget->setLayout(_layout);
QScrollArea* scroll = new QScrollArea();
scroll->setWidget(widget);
scroll->show();
}
This doesn't seem easy. Basically, I add QPushButton
s through a function to a layout, and when the function executes, I want to clear the layout first (removing all QPushButton
s and whatever else is in there), because more buttons just get appended to the scrollview
.
header
QVBoxLayout* _layout;
cpp
void MainWindow::removeButtonsThenAddMore(const QString &item) {
//remove buttons/widgets
QVBoxLayout* _layout = new QVBoxLayout(this);
QPushButton button = new QPushButton(item);
_layout->addWidget(button);
QPushButton button = new QPushButton("button");
_layout->addWidget(button);
QWidget* widget = new QWidget();
widget->setLayout(_layout);
QScrollArea* scroll = new QScrollArea();
scroll->setWidget(widget);
scroll->show();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
我遇到了同样的问题:我有一个游戏应用程序,其主窗口类继承 QMainWindow。它的构造函数看起来像这样:
当我想要显示游戏的一个级别时,我实例化一个 QGridLayout,在其中添加 QLabels,然后将它们的像素图设置为某些图片(具有透明部分的像素图)。第一个级别显示正常,但是当切换到第二个级别时,仍然可以在新级别的后面看到第一个级别的像素图(其中像素图是透明的)。
我尝试了几种方法来删除旧的小部件。 (a) 我尝试删除 QGridLayout 并实例化一个新的 QGridLayout,但随后了解到删除布局不会删除添加到其中的小部件。 (b) 我尝试在新的像素图上调用 QLabel::clear() ,但这当然只对新的像素图有影响,对僵尸像素图没有影响。 (c) 我什至尝试删除我的 m_view 和 m_scene,并在每次显示新关卡时重建它们,但仍然没有成功。
然后(d)我尝试了上面给出的解决方案之一,
但这也不起作用。
然而,进一步谷歌搜索,我找到了一个有效的答案。 (d) 中缺少的是对
delete item->widget()
的调用。下面的内容现在对我有用:然后我像第一个级别一样实例化一个新的 QGridLayout,向其中添加新级别的小部件等。Qt
在很多方面都很棒,但我确实认为这个问题表明事情可能有点这里更容易。
I had the same problem: I have a game app whose main window class inherits QMainWindow. Its constructor looks partly like this:
When I want to display a level of the game, I instantiate a QGridLayout, into which I add QLabels, and then set their pixmaps to certain pictures (pixmaps with transparent parts). The first level displays fine, but when switching to the second level, the pixmaps from the first level could still be seen behind the new ones (where the pixmap was transparent).
I tried several things to delete the old widgets. (a) I tried deleting the QGridLayout and instantiating a new one, but then learned that deleting a layout does not delete the widgets added to it. (b) I tried calling QLabel::clear() on the new pixmaps, but that of course had only an effect on the new ones, not the zombie ones. (c) I even tried deleting my m_view and m_scene, and reconstructing them every time I displayed a new level, but still no luck.
Then (d) I tried one of the solutions given above, namely
but that didn't work, either.
However, googling further, I found an answer that worked. What was missing from (d) was a call to
delete item->widget()
. The following now works for me:and then I instantiate a new QGridLayout as with the first level, add the new level's widgets to it, etc.
Qt is great in many ways, but I do think this problems shows that things could be a bit easier here.
Qt帮助中的布局管理页面指出:
我的结论: Widget 需要手动销毁或通过销毁父 WIDGET,而不是布局来销毁
我的结论: 与上面相同
对于@Muelner 的“矛盾”“项目的所有权已转移到布局,布局有责任删除它。” - 这并不意味着 WIDGET,而是重新设置布局的 ITEM,并且稍后将被布局删除。小部件仍然是安装布局的小部件的子级,需要手动或通过删除整个父小部件来删除它们。
如果确实需要从布局中删除所有小部件和项目,使其完全为空,那么他需要创建一个如下的递归函数:
Layout management page in Qt's help states:
My conclusion: Widgets need to be destroyed manually or by destroying the parent WIDGET, not layout
My conclusion: Same as above
To @Muelner for "contradiction" "The ownership of item is transferred to the layout, and it's the layout's responsibility to delete it." - this doesn't mean WIDGET, but ITEM that is reparented to the layout and will be deleted later by the layout. Widgets are still children of the widget the layout is installed on, and they need to be removed either manually or by deleting the whole parent widget.
If one really needs to remove all widgets and items from a layout, leaving it completely empty, he needs to make a recursive function like this:
此代码删除其所有子项。因此布局内的所有内容都会“消失”。
这将删除
yourWidget
小部件的所有直接小部件。使用Qt::FindDirectChildrenOnly
是必需的,因为它可以防止删除同样在列表中并且可能已被内的循环删除的小部件的子部件。 qDeleteAll
。下面是
qDeleteAll
的描述:请注意,需要使用该小部件(而不是布局)中的容器来调用 qDeleteAll 。请注意,
qDeleteAll
不会删除yourWidget
- 只是删除它的子项。This code deletes all its children. So everything inside the layout 'disappears'.
This deletes all direct widgets of the widget
yourWidget
. UsingQt::FindDirectChildrenOnly
is essential as it prevents the deletion of widgets that are children of widgets that are also in the list and probably already deleted by the loop insideqDeleteAll
.Here is the description of
qDeleteAll
:Note that
qDeleteAll
needs to be called with a container from that widget (not the layout). And note thatqDeleteAll
does NOT deleteyourWidget
- just its children.未经尝试:为什么不创建一个新布局,将其与旧布局交换并删除旧布局?这应该删除布局拥有的所有项目并保留其他项目。
编辑:在研究了我的答案的评论、文档和 Qt 源代码之后,我找到了一个更好的解决方案:
如果您仍然启用了 Qt3 支持,您可以使用 QLayout::deleteAllItems() ,它基本上是与 QLayout::takeAt 文档中的提示相同:
编辑:经过进一步研究,上面的两个版本似乎是等效的:仅删除没有父级的子布局和小部件。具有父级的小部件以特殊方式处理。看起来 TeL 的解决方案应该有效,您只需小心不要删除任何顶级小部件。另一种方法是使用小部件层次结构来删除小部件:创建一个没有父部件的特殊小部件,并将所有可删除的小部件创建为该特殊小部件的子部件。清理布局后删除这个特殊的小部件。
Untried: Why not create a new layout, swap it with the old layout and delete the old layout? This should delete all items that were owned by the layout and leave the others.
Edit: After studying the comments to my answer, the documentation and the Qt sources I found a better solution:
If you still have Qt3 support enabled, you can use QLayout::deleteAllItems() which is basically the same as hint in the documentation for QLayout::takeAt:
Edit: After further research it looks like both version above are equivalent: only sublayouts and widget without parents are deleted. Widgets with parent are treated in a special way. It looks like TeL's solution should work, you only should be careful not to delete any top-level widgets. Another way would be to use the widget hierarchy to delete widgets: Create a special widget without parent and create all your removeable widgets as child of this special widget. After cleaning the layout delete this special widget.
您还需要确保删除间隔符和非 QWidget 的内容。如果您确定布局中唯一的东西是 QWidget,那么前面的答案就很好。否则您应该这样做:
了解如何执行此操作很重要,因为如果您要清除的布局是更大布局的一部分,您不想破坏该布局。您需要确保您的布局保持其位置以及与窗口其余部分的关系。
您还应该小心,每次调用此方法时都会创建大量对象,并且它们不会被清理。首先,您可能应该在其他地方创建 QWidget 和 QScrollArea,并保留一个指向它们的成员变量以供参考。那么你的代码可能看起来像这样:
You also want to make sure that you remove spacers and things that are not QWidgets. If you are sure that the only things in your layout are QWidgets, the previous answer is fine. Otherwise you should do this:
It is important to know how to do this because if the layout you want to clear is part of a bigger layout, you don't want to destroy the layout. You want to ensure that your layout maintains it's place and relation to the rest of your window.
You should also be careful, you're are creating a load of objects each time you call this method, and they are not being cleaned up. Firstly, you probably should create the QWidget and QScrollArea somewhere else, and keep a member variable pointing to them for reference. Then your code could look something like this:
您不会写到另一种方式,但您也可以只使用 QStackedWidget 并向其中添加两个视图,每个视图对应您需要的按钮排列。在这两个按钮之间切换就不是问题了,而且比处理动态创建的按钮的各种实例的风险要小得多
You do not write about going the other way, but you could also just use a
QStackedWidget
and add two views to this, one for each arrangement of buttons that you need. Flipping between the two of them is a non issue then and a lot less risk than juggling various instances of dynamically created buttons现有的答案都不适用于我的应用程序。到目前为止,对达科·马克西莫维奇的修改似乎有效。就是这样:
至少对于我的小部件和布局的层次结构来说,有必要递归并显式删除小部件。
None of the existing answers worked in my application. A modification to Darko Maksimovic's appears to work, so far. Here it is:
It was necessary, at least with my hierarchy of widgets and layouts, to recurse and to delete widgets explicity.
仅适用于我的按钮列表,如果小部件本身也被删除。否则旧按钮仍然可见:
only works for my buttonlist, if the widgets themeselves are deleted, too. otherwise the old buttons are still visible:
我有一个类似的情况,我有一个
QVBoxLayout
,其中包含动态创建的QHBoxLayout
对象,其中包含许多QWidget
实例。由于某种原因,我无法通过删除顶级 QVBoxLayout 或单个 QHBoxLayout 来摆脱小部件。我工作的唯一解决方案是遍历层次结构并专门删除和删除所有内容:I had a similar case where I have a
QVBoxLayout
containing dynamically createdQHBoxLayout
objects containing a number ofQWidget
instances. For some reason I couldn't get rid of the widgets either by deleting neither the top level QVBoxLayout or the individual QHBoxLayouts. The only solution I got to work was by going through the hierarchy and removing and deleting everything specifically:如果你想删除所有小部件,你可以这样做:
If you want to remove all widgets, you could do something like this:
我对这个问题有一个可能的解决方案(请参阅 Qt - 清除 QWidget 布局内的所有小部件)。通过两个单独的步骤删除所有小部件和布局。
第 1 步:删除所有小部件
第 2 步:删除所有布局
在第 2 步之后,您的 MYTOPWIDGET 应该是干净的。
I have a possible solution for this problem (see Qt - Clear all widgets from inside a QWidget's layout). Delete all widgets and layouts in two seperate steps.
Step 1: Delete all widgets
Step 2: Delete all layouts
After step 2 your MYTOPWIDGET should be clean.
如果您在将小部件添加到布局以及将布局添加到其他布局时没有做任何有趣的事情,那么它们都应该在添加到其父小部件时重新设置父级。所有
QObject
都有一个deleteLater()
槽,一旦控制权返回到事件循环,该槽就会导致QObject
被删除。在此 Manor 中删除的小部件也会删除其子项。因此,您只需对树中最高的项目调用deleteLater()
即可。hpp 中
在cpp 中的
还请注意,在您的示例中,
_layout
是一个局部变量,与头文件中的_layout
不同(删除QVBoxLayout*
部分)。另请注意,以_
开头的名称是为标准库实现者保留的。我使用尾随_
(如var_
中所示)来显示局部变量,有多种口味,但前面的_
和__
技术上是保留的。If you don't do anything funny when adding widgets to layouts and layouts to other layouts they should all be reparented upon addition to their parent widget. All
QObject
s have adeleteLater()
slot which will cause theQObject
to be deleted as soon as control is returned to the event loop. Widgets deleted in this Manor also delete their children. Therefore you simply need to calldeleteLater()
on the highest item in the tree.in hpp
in cpp
also note that in your example the
_layout
is a local variable and not the same thing as the_layout
in the header file (remove theQVBoxLayout*
part). Also note that names beginning with_
are reserved for standard library implementers. I use trailing_
as invar_
to show a local variable, there are many tastes but preceding_
and__
are technically reserved.PyQt5 中存在某种错误,如果您使用上述方法,布局中仍会显示未删除的项目。所以我只是删除布局并重新开始:
例如
There's some sort of bug in PyQt5 where if you employ the above methods, there remain undeleted items shown in the layout. So I just delete the layout and start over:
E.g.