C++ Qt - 从布局中删除所有小部件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4272196/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Qt - remove all widgets from layout?
提问by
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
.
这似乎并不容易。基本上,我QPushButton
通过一个函数将s添加到布局中,当该函数执行时,我想先清除布局(删除所有QPushButton
s 以及其中的所有其他内容),因为更多按钮会附加到scrollview
.
header
标题
QVBoxLayout* _layout;
cpp
cp
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();
}
回答by Teemu Leisti
I had the same problem: I have a game app whose main window class inherits QMainWindow. Its constructor looks partly like this:
我有同样的问题:我有一个游戏应用程序,它的主窗口类继承了 QMainWindow。它的构造函数部分看起来像这样:
m_scene = new QGraphicsScene;
m_scene->setBackgroundBrush( Qt::black );
...
m_view = new QGraphicsView( m_scene );
...
setCentralWidget( m_view );
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).
当我想显示游戏的某个关卡时,我会实例化一个 QGridLayout,在其中添加 QLabels,然后将它们的像素图设置为某些图片(具有透明部分的像素图)。第一层显示正常,但是当切换到第二层时,仍然可以在新层后面看到第一层的像素图(像素图是透明的)。
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.
我尝试了几种方法来删除旧的小部件。(a) 我尝试删除 QGridLayout 并实例化一个新的,但后来了解到删除布局不会删除添加到其中的小部件。(b) 我尝试在新的像素图上调用 QLabel::clear() ,但这当然只对新像素有影响,对僵尸像素没有影响。(c) 我什至尝试删除我的 m_view 和 m_scene,并在每次显示新关卡时重建它们,但仍然没有运气。
Then (d) I tried one of the solutions given above, namely
然后(d)我尝试了上面给出的解决方案之一,即
QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
delete wItem;
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:
但是,进一步使用谷歌搜索,我找到了一个有效的答案。(d) 中缺少的是对 的调用delete item->widget()
。以下现在对我有用:
// THIS IS THE SOLUTION!
// Delete all existing widgets, if any.
if ( m_view->layout() != NULL )
{
QLayoutItem* item;
while ( ( item = m_view->layout()->takeAt( 0 ) ) != NULL )
{
delete item->widget();
delete item;
}
delete m_view->layout();
}
and then I instantiate a new QGridLayout as with the first level, add the new level's widgets to it, etc.
然后我像第一个级别一样实例化一个新的 QGridLayout,向其中添加新级别的小部件,等等。
Qt is great in many ways, but I do think this problems shows that things could be a bit easier here.
Qt 在很多方面都很棒,但我确实认为这个问题表明这里的事情可能会更容易一些。
回答by Darko Maksimovic
Layout managementpage in Qt's help states:
Qt 帮助中的布局管理页面指出:
The layout will automatically reparent the widgets (using QWidget::setParent()) so that they are children of the widget on which the layout is installed.
布局将自动重新设置小部件的父级(使用 QWidget::setParent()),以便它们是安装了布局的小部件的子级。
My conclusion:Widgets need to be destroyed manually or by destroying the parent WIDGET, not layout
我的结论:小部件需要手动销毁或通过销毁父 WIDGET,而不是布局
Widgets in a layout are children of the widget on which the layout is installed, not of the layout itself. Widgets can only have other widgets as parent, not layouts.
布局中的小部件是安装了布局的小部件的子代,而不是布局本身的子代。小部件只能有其他小部件作为父小部件,而不是布局。
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.
致@Muelner 的“矛盾”“项目的所有权已转移到布局,布局有责任将其删除。” - 这并不意味着 WIDGET,而是重新指向布局的 ITEM,稍后将被布局删除。小部件仍然是安装布局的小部件的子部件,需要手动或通过删除整个父小部件来删除它们。
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:
如果真的需要从布局中删除所有小部件和项目,使其完全为空,他需要创建一个这样的递归函数:
// shallowly tested, seems to work, but apply the logic
void clearLayout(QLayout* layout, bool deleteWidgets = true)
{
while (QLayoutItem* item = layout->takeAt(0))
{
if (deleteWidgets)
{
if (QWidget* widget = item->widget())
widget->deleteLater();
}
if (QLayout* childLayout = item->layout())
clearLayout(childLayout, deleteWidgets);
delete item;
}
}
回答by m.w.
This code deletes all its children. So everything inside the layout 'disappears'.
此代码删除其所有子项。所以布局内的所有东西都“消失”了。
qDeleteAll(yourWidget->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly));
This deletes all directwidgets of the widget yourWidget
. Using Qt::FindDirectChildrenOnly
is essentialas it prevents the deletion of widgets that are children of widgets that are also in the list and probably already deleted by the loop inside qDeleteAll
.
这将删除小部件的所有直接小部件yourWidget
。使用Qt::FindDirectChildrenOnly
是必不可少的,因为它可以防止删除同样在列表中并且可能已经被内部循环删除的小部件的子部件qDeleteAll
。
Here is the description of qDeleteAll
:
以下是对 的描述qDeleteAll
:
void qDeleteAll(ForwardIterator begin, ForwardIterator end)
Deletes all the items in the range [begin, end] using the C++ delete > operator. The item type must be a pointer type (for example, QWidget *).
void qDeleteAll(ForwardIterator 开始,ForwardIterator 结束)
使用 C++ 删除 > 运算符删除 [begin, end] 范围内的所有项目。项目类型必须是指针类型(例如,QWidget *)。
Note that qDeleteAll
needs to be called with a container from that widget (not the layout). And note that qDeleteAll
does NOT delete yourWidget
- just its children.
请注意,qDeleteAll
需要使用来自该小部件(而不是布局)的容器来调用。请注意,qDeleteAll
不会删除yourWidget
- 只是它的孩子。
回答by hmuelner
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:
编辑:在研究了对我的答案、文档和 Qt 源的评论后,我找到了一个更好的解决方案:
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:
如果您仍然启用了 Qt3 支持,则可以使用 QLayout::deleteAllItems(),它与 QLayout::takeAt 文档中的提示基本相同:
The following code fragment shows a safe way to remove all items from a layout:
以下代码片段显示了从布局中删除所有项目的安全方法:
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
...
delete child;
}
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.
编辑:经过进一步研究,看起来上面的两个版本是等效的:只有子布局和没有父级的小部件被删除。带有父级的小部件以特殊方式处理。看起来 TeL 的解决方案应该可行,您只需要注意不要删除任何顶级小部件。另一种方法是使用小部件层次结构来删除小部件:创建一个没有父级的特殊小部件,并将所有可删除的小部件创建为这个特殊小部件的子级。清理布局后删除这个特殊的小部件。
回答by Liz
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:
您还需要确保删除间隔器和不是 QWidget 的东西。如果您确定布局中唯一的东西是 QWidgets,那么前面的答案就可以了。否则你应该这样做:
QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
delete wItem;
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:
您还应该小心,每次调用此方法时都会创建大量对象,并且不会清除它们。首先,您可能应该在其他地方创建 QWidget 和 QScrollArea,并保留一个指向它们的成员变量以供参考。那么您的代码可能如下所示:
QLayout *_layout = WidgetMemberVariable->layout();
// If it is the first time and the layout has not been created
if (_layout == 0)
{
_layout = new QVBoxLayout(this);
WidgetMemberVariable->setLayout(_layout);
}
// Delete all the existing buttons in the layout
QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
delete wItem;
// Add your new buttons here.
QPushButton button = new QPushButton(item);
_layout->addWidget(button);
QPushButton button = new QPushButton("button");
_layout->addWidget(button);
回答by Martin Wilde
only works for my buttonlist, if the widgets themeselves are deleted, too. otherwise the old buttons are still visible:
如果小部件本身也被删除,则仅适用于我的按钮列表。否则旧按钮仍然可见:
QLayoutItem* child;
while ((child = pclLayout->takeAt(0)) != 0)
{
if (child->widget() != NULL)
{
delete (child->widget());
}
delete child;
}
回答by Harald Scheirich
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
您不会写到另一种方式,但您也可以只使用 aQStackedWidget
并为此添加两个视图,一个用于您需要的每种按钮排列。在这两者之间切换是没有问题的,而且比处理各种动态创建的按钮实例的风险要小得多
回答by Alex S
None of the existing answers worked in my application. A modification to Darko Maksimovic's appears to work, so far. Here it is:
现有的答案都不适用于我的应用程序。到目前为止,对 Darko Maksimovic 的修改似乎奏效了。这里是:
void clearLayout(QLayout* layout, bool deleteWidgets = true)
{
while (QLayoutItem* item = layout->takeAt(0))
{
QWidget* widget;
if ( (deleteWidgets)
&& (widget = item->widget()) ) {
delete widget;
}
if (QLayout* childLayout = item->layout()) {
clearLayout(childLayout, deleteWidgets);
}
delete item;
}
}
It was necessary, at least with my hierarchy of widgets and layouts, to recurse and to delete widgets explicity.
至少在我的小部件和布局层次结构中,有必要明确地递归和删除小部件。
回答by teukkam
I had a similar case where I have a QVBoxLayout
containing dynamically created QHBoxLayout
objects containing a number of QWidget
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:
我有一个类似的案例,我有一个QVBoxLayout
包含QHBoxLayout
多个QWidget
实例的动态创建的对象。出于某种原因,我无法通过删除顶层 QVBoxLayout 或单个 QHBoxLayouts 来摆脱小部件。我开始工作的唯一解决方案是通过层次结构并专门删除和删除所有内容:
while(!vbox->isEmpty()) {
QLayout *hb = vbox->takeAt(0)->layout();
while(!hb->isEmpty()) {
QWidget *w = hb->takeAt(0)->widget();
delete w;
}
delete hb;
}
回答by odinthenerd
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 a deleteLater()
slot which will cause the QObject
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 call deleteLater()
on the highest item in the tree.
如果您在将小部件添加到布局和布局到其他布局时没有做任何有趣的事情,它们都应该在添加到其父小部件时重新设置父级。所有QObject
s 都有一个deleteLater()
插槽,QObject
一旦控制权返回到事件循环,就会导致删除。在此庄园中删除的小部件也会删除其子部件。因此,您只需要调用deleteLater()
树中最高的项目。
in hpp
马力
QScrollArea * Scroll;
in cpp
在 cpp
void ClearAndLoad(){
Scroll->widget()->deleteLater();
auto newLayout = new QVBoxLayout;
//add buttons to new layout
auto widget = new QWidget;
widget->setLayout(newLayout);
Scroll->setWidget(widget);
}
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 the QVBoxLayout*
part). Also note that names beginning with _
are reserved for standard library implementers. I use trailing _
as in var_
to show a local variable, there are many tastes but preceding _
and __
are technically reserved.
还请注意,在您的示例中, the_layout
是一个局部变量_layout
,与头文件中的 the 不同(删除QVBoxLayout*
部分)。另请注意,以 开头的名称_
是为标准库实现者保留的。我使用尾随_
invar_
来显示局部变量,有很多口味但在前面_
并且__
在技术上是保留的。