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

时间:2010-11-24 22:37:10

标签: c++ qt mobile qt4 nokia

这似乎并不容易。基本上,我通过函数将QPushButtons添加到布局中,当函数执行时,我想首先清除布局(删除所有QPushButtons以及其中的任何其他内容),因为更多按钮只会附加到滚动视图。

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

}

12 个答案:

答案 0 :(得分:40)

我遇到了同样的问题:我有一个游戏应用程序,其主窗口类继承了QMainWindow。它的构造函数看起来像这样:

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

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

我尝试了几件事来删除旧的小部件。 (a)我尝试删除QGridLayout并实例化一个新的,但后来了解到删除布局不会删除添加到它的小部件。 (b)我尝试在新的pixmaps上调用QLabel :: clear(),但这当然只会影响新的像素,而不会影响僵尸。 (c)我甚至尝试删除我的m_view和m_scene,并在每次显示新级别时重建它们,但仍然没有运气。

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

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

但这也行不通。

然而,谷歌搜索更进一步,我找到了一个有效的答案。 (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();
}

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

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

答案 1 :(得分:35)

Qt帮助中的

Layout management页面说明:

  

布局将自动重新显示小部件(使用   QWidget :: setParent())以便它们是小部件的子节点   布局已安装。

我的结论:小工具需要手动销毁或销毁父WIDGET,而不是布局

  

布局中的窗口小部件是窗口小部件的子窗口   布局是安装的,而不是布局本身。小部件只能有   其他小部件作为父级,而不是布局。

我的结论:与上述相同

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

如果确实需要从布局中删除所有小部件和项目,将其完全清空,他需要创建这样的递归函数:

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

答案 2 :(得分:10)

我知道这个问题很老并且已经回答了,但是:由于QtAlgorithms提供了qDeleteAll,因此可以删除布局,包括使用单行删除所有子代。 此代码删除了布局,其所有子项以及布局中的所有内容都消失了。

qDeleteAll(yourWidget->children());

以下是重载函数的说明:

  

void qDeleteAll(ForwardIterator begin,ForwardIterator end)

     

使用C ++ delete>删除[开始,结束]范围内的所有项目。运营商。项类型必须是指针类型(例如,QWidget *)。

请注意,qDeleteAll需要从该窗口小部件(而不是该布局)提供容器。请注意,qDeleteAll不会删除yourWidget - 只删除它的子项。

现在可以设置新的布局。

答案 3 :(得分:8)

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

编辑:在对我的回答,文档和Qt资源的评论进行研究后,我找到了更好的解决方案:

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

  

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

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

编辑:经过进一步研究后,看起来上述两个版本都是等效的:只删除子布局和没有父项的窗口小部件。父母的小部件以特殊方式处理。看起来TeL的解决方案应该可行,您应该注意不要删除任何顶级小部件。另一种方法是使用窗口小部件层次结构删除窗口小部件:创建一个没有父窗口的特殊窗口小部件,并创建所有可移动窗口小部件作为此特殊窗口小部件的子窗口。清理布局后,删除此特殊小部件。

答案 4 :(得分:4)

您还需要确保删除间隔符和非QWidgets的内容。如果您确定布局中的唯一内容是QWidgets,那么之前的答案就可以了。否则你应该这样做:

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

知道如何执行此操作非常重要,因为如果要清除的布局是更大布局的一部分,则不希望破坏布局。您希望确保您的布局保持它与窗口其余部分的位置和关系。

您还应该小心,每次调用此方法时,您都在创建一堆对象,并且它们没有被清理。首先,您可能应该在其他地方创建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);

答案 5 :(得分:2)

你不会写另一种方式,但你也可以只使用一个QStackedWidget并为此添加两个视图,每个视图用于您需要的每个按钮排列。在他们两个之间翻转是一个非问题,并且比玩弄动态创建的按钮的各种实例的风险要小得多

答案 6 :(得分:2)

我的申请中没有现有的答案。到目前为止,对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;
    }
}

至少在我的小部件和布局层次结构中,有必要递归和删除小部件明显。

答案 7 :(得分:2)

仅适用于我的按钮列表,如果小部件本身也被删除。否则旧按钮仍然可见:

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

答案 8 :(得分:1)

我有一个类似的情况,我有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;
}

答案 9 :(得分:0)

如果要删除所有小部件,可以执行以下操作:

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

答案 10 :(得分:0)

如果在将布局和布局添加到其他布局的窗口小部件时没有做任何有趣的事情,那么除了它们的父窗口小部件之外,它们都应该被重新设置。所有QObject都有一个deleteLater()槽,一旦控件返回到事件循环,就会导致删除Object。在这个庄园中删除的小部件也会删除他们的孩子。因此,您只需要在树中的最高项上调用deleteLater()。

in hpp

QScrollArea * Scroll;

in 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是一个局部变量,与头文件中的_layout不同(删除QVBoxLayout*部分)。另请注意,以_开头的名称保留给标准库实现者。我在var_中使用尾随_来显示一个局部变量,有许多品味,但前面的_和__在技术上是保留的。

答案 11 :(得分:0)

我有解决此问题的可能方法(请参阅Qt - Clear all widgets from inside a QWidget's layout)。以两个单独的步骤删除所有小部件和布局。

第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应该是干净的。