Qt – 从布局中删除所有小部件?

这似乎并不容易。 基本上,我通过一个函数添加QPushButtons到一个布局,当函数执行时,我想首先清除布局(删除所有QPushButtons和其他内部),因为更多的button只是附加到滚动视图。

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

我有同样的问题:我有一个游戏的应用程序,其主窗口类inheritanceQMainWindow。 它的构造函数看起来部分是这样的:

 m_scene = new QGraphicsScene; m_scene->setBackgroundBrush( Qt::black ); ... m_view = new QGraphicsView( m_scene ); ... setCentralWidget( m_view ); 

当我想要显示一个游戏级别时,我实例化一个QGridLayout,在其中添加QLabels,然后将它们的pixmaps设置为某些图片(带有透明部分的pixmaps)。 第一层显示正常,但切换到第二层时,第一层的像素图仍然可以在新像素之后(像素图透明的位置)看到。

我尝试了几件事删除旧的小部件。 (a)我尝试删除QGridLayout并实例化一个新的,但后来得知,删除布局不会删除添加到它的小部件。 (b)我试图在新的pixmaps上调用QLabel :: clear(),但是这当然只对新的有影响,而不是僵尸。 (三)我甚至尝试删除我的m_view和m_scene,并重新构build他们,每次我展示一个新的水平,但仍然没有运气。

然后(d)我尝试了上面给出的解决scheme之一,即

 QLayoutItem *wItem; while (wItem = widget->layout()->takeAt(0) != 0) delete wItem; 

但是那也行不通。

然而,进一步g​​ooglesearch, 我find了一个有效的答案 。 (d)中缺less的是调用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(); } 

然后我实例化一个新的QGridLayout与第一个级别,添加新的级别的小部件,等等。

Qt在许多方面都很棒,但我认为这个问题表明事情在这里可能会更容易些。

Qt帮助中的“布局pipe理”页面指出:

该布局将自动重新部署小部件(使用QWidget :: setParent()),以便它们是安装布局的小部件的子项。 我的结论是:小部件需要手动销毁或销毁父WIDGET,而不是布局

布局中的小部件是安装布局的小部件的子部件,而不是布局本身。 小部件只能将其他小部件作为父项,而不是布局。 我的结论是:同上

到@Muelner的“矛盾”“项目的所有权转移到布局,这是布局的责任,删除它”。 – 这并不意味着WIDGET,而是重新排版到布局的ITEM,并在之后的布局中被删除。 小部件仍然是布局安装的小部件的子项,需要手动删除它们或删除整个父小部件。

如果真的需要删除布局中的所有小部件和项目,将其完全清空,则需要像这样做一个recursion函数://浅testing,似乎工作,但应用逻辑

 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; } } 

未尝试:为什么不创build一个新的布局,交换与旧的布局,并删除旧的布局? 这应该删除布局拥有的所有项目,并保留其他项目。

编辑 :在研究了我的答案,文档和Qt的来源的意见后,我发现了一个更好的解决scheme:

如果你仍然支持Qt3的支持,你可以使用QLayout :: deleteAllItems(),这与QLayout :: takeAt的文档中的提示基本相同:

以下代码片段显示了从布局中删除所有项目的安全方法:

 QLayoutItem *child; while ((child = layout->takeAt(0)) != 0) { ... delete child; } 

编辑 :经过进一步的研究,它看起来像上面的两个版本是相同的:只有sublayouts和没有父母的部件被删除。 与父母的小工具以特殊的方式处理。 它看起来像TeL的解决scheme应该工作,你只应该小心不要删除任何顶级小部件。 另一种方法是使用小部件层次结构来删除小部件:创build一个没有父项的特殊小部件,并创build所有可移除小部件作为此特殊小部件的子项。 清理布局后,删除这个特殊的小部件。

我知道这个问题是旧的和回答,但是:由于QtAlgorithms提供qDeleteAll,可以删除一个布局,包括删除所有的孩子一个class轮。 这段代码删除布局,其所有的孩子和布局内的一切“消失”。

 qDeleteAll(yourWidget->children()); 

这里是重载函数的描述:

void qDeleteAll(ForwardIterator begin,ForwardIterator end)

使用C ++ delete>操作符删除范围[begin,end]中的所有项目。 项目types必须是指针types(例如QWidget *)。

请注意,qDeleteAll需要从该小部件的容器(而不是布局)。 并注意,qDeleteAll不会删除你的yourWidget – 只是它的孩子。

现在可以设置新的布局。

你也想确保你删除了不是QWidgets的间隔符和东西。 如果你确定你的布局中唯一的东西是QWidgets,那么以前的答案是好的。 否则,你应该这样做:

 QLayoutItem *wItem; while (wItem = widget->layout()->takeAt(0) != 0) delete wItem; 

知道如何做到这一点很重要,因为如果要清除的布局是更大布局的一部分,则不要破坏布局。 你想确保你的布局保持它的位置和与其他窗口的关系。

你也应该小心,你每次调用这个方法时都会创build一个对象的负载,而且他们没有被清理。 首先,您可能应该在其他地方创buildQWidget和QScrollArea,并保留一个成员variables指向它们以供参考。 那么你的代码可能看起来像这样:

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

你不会写另一种方式,但是你也可以使用一个QStackedWidgetQStackedWidget添加两个视图,一个用于你需要的button的排列。 在两者之间翻转并不是什么问题,与dynamic创buildbutton的各种实例相比,风险要小得多

现有的答案没有在我的应用程序工作。 到目前为止,对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; } } 

至less在我的小部件和布局层次结构中,有必要recursion和删除小部件明确性。

只适用于我的button列表,如果小部件自己也被删除。 否则旧的button仍然可见:

 QLayoutItem* child; while ((child = pclLayout->takeAt(0)) != 0) { if (child->widget() != NULL) { delete (child->widget()); } delete child; } 

我有一个类似的情况,我有一个QVBoxLayout包含dynamic创buildQHBoxLayout对象包含一些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; } 

如果你想删除所有的小部件,你可以做这样的事情:

 foreach (QObject *object, _layout->children()) { QWidget *widget = qobject_cast<QWidget*>(object); if (widget) { delete widget; } } 

如果在将布局和布局添加到其他布局时不做任何有趣的事情,则应该在添加到其父布局时重新进行重新布局。 所有的QObject都有一个deleteLater()槽,一旦控制返回到事件循环,这个槽就会导致对象被删除。 在这个庄园删除的小部件也删除他们的孩子。 因此,只需调用树中最高项的deleteLater()即可。

在hpp

 QScrollArea * Scroll; 

在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); } 

还要注意,在你的例子中, _layout是一个局部variables,与头文件中的QVBoxLayout* (删除QVBoxLayout*部分)。 还要注意,以_开头的名字是为标准库实现者保留的。 我在var_使用尾部_来显示一个局部variables,有许多口味,但是前面的_和__在技术上是保留的。

我有这个问题的可能的解决scheme(请参阅Qt – 清除QWidget的布局中的所有小部件 )。 在两个单独的步骤中删除所有小部件和布局。

第1步:删除所有小部件

  QList< QWidget* > children; do { children = MYTOPWIDGET->findChildren< QWidget* >(); if ( children.count() == 0 ) break; delete children.at( 0 ); } while ( true ); 

第2步:删除所有布局

  if ( MYTOPWIDGET->layout() ) { QLayoutItem* p_item; while ( ( p_item = MYTOPWIDGET->layout()->takeAt( 0 ) ) != nullptr ) delete p_item; delete MYTOPWIDGET->layout(); } 

第2步之后,MYTOPWIDGET应该是干净的。