Qt - 清除QWidget布局中的所有小部件

9

我在一个对话框中有一个 QWidget 。在程序运行的过程中,会像这样添加几个 QCheckBox * 对象到布局中:

QCheckBox *c = new QCheckBox("Checkbox text");
ui->myWidget->layout()->addWidget(c);

这对于所有复选框都很好用。但是,我在我的对话框中还有一个名为“清除”的QPushButton,当它被按下时,应该清空myWidget中的所有内容,使其变为空白,就像在添加任何QCheckBoxes之前一样。我在网上和文档中寻找了一些方法,但我很难找到一种方法来做到这一点。我发现这个问题与我的问题类似,并尝试使用他们的解决方案,如下所示:
void myClass::on_clear_clicked()
{
  while(ui->myWidget->layout()->count() > 0)
  {
    QLayoutItem *item = ui->myWidget->layout()->takeAt(0);
    delete item;
  }
}

然而,这似乎没有起作用。 值得注意的是,我不确定是否正确翻译了他的答案; 给定的函数应该如何实现有点不清楚,所以我尽力做出了最好的猜测。 如果有人知道我可以更改以上内容以使其工作(或仅通过其他方法使其工作),那将不胜感激。

4个回答

14

你可以尝试这个:

    while ( QLayoutItem* item = ui->myWidget->layout()->takeAt( 0 ) )
    {
        Q_ASSERT( ! item->layout() ); // otherwise the layout will leak
        delete item->widget();
        delete item;
    }

抱歉,我以为这个程序已经可以运行了,但实际上并没有 :/ 还是谢谢你。 - thnkwthprtls
如果布局项是一个间隔符或子布局,则item->widget()返回0。这会导致崩溃。 - galinette
这个方法可以工作,但你需要检查 item->widget() 是否有效,并且最好调用对象的 deleteLater 而不是直接删除它。只需使用 if (auto widget = item->widget()) widget->deleteLater(); 来修补该行即可。 - iKlsR
@iKlsR 都不是真的:当传递一个空指针时,delete有定义的行为——它什么也不做。随时删除小部件都没问题,除非此代码位于从小部件调用的槽内,并且它使用了小部件上下文(��� this)。 - Kuba hasn't forgotten Monica
通过添加Q_ASSERT(sender() != item->widget() || ! sender()),此代码将变得稍微更有弹性,这将避免由于删除发送器部件而导致难以调试的问题。在这种情况下,硬性失败要容易得多。这不会对发布版本产生任何性能影响。 - Kuba hasn't forgotten Monica
@KubaOber 不是不同意,只是记不得在哪里用过这个了。我记得的是,在调用delete时,Qt会抛出异常。我还记得从文档中读到过推迟删除的建议,所以可能是某些奇怪的情况下小部件正在使用中。 - iKlsR

10

布局的好处在于它们可以自动处理小部件的删除。所以你只需要迭代所有的小部件就可以了。如果你想清除给定小部件的所有子元素,只需执行:

for (auto widget: ui->myWidget::findChildren<QWidget*>
                                            ({}, Qt::FindDirectChildrenOnly))
  delete widget;

毫不担心布局问题。无论子项是否由布局管理,都可以正常工作。

如果想要非常准确,您需要忽略那些是子窗口但又是独立窗口的小部件。如果这是通用库代码,则会出现这种情况:

for (auto widget: ui->myWidget::findChildren<QWidget*>
                                            ({}, Qt::FindDirectChildrenOnly)) 
  if (! widget->windowFlags() & Qt::Window) delete widget;

或者,如果您只想删除特定布局及其子布局所管理的子项:

void clearWidgets(QLayout * layout) {
   if (! layout)
      return;
   while (auto item = layout->takeAt(0)) {
      delete item->widget();
      clearWidgets(item->layout());
   }
}

clearWidgets递归函数是我找到的唯一能够删除以编程方式创建的嵌套布局/小部件的方法。没有UI句柄使事情变得困难,但上述函数是我所需要的秘密武器! - Neil Ruggiero

2

考虑到你有一个包含小部件的级联布局的小部件层次结构,那么你应该采取以下步骤。

第一步:删除所有小部件

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

第二步:删除所有布局。
    if ( MYTOPWIDGET->layout() )
    {
        QLayoutItem* p_item;
        while ( ( p_item = MYTOPWIDGET->layout()->takeAt( 0 ) ) != nullptr )
            delete p_item;
        delete MYTOPWIDGET->layout();
    }

在第二步完成后,您的MYTOPWIDGET应该是干净的。

0

PySide2 解决方案:

from PySide2 import QtWidgets


def clearLayout(layout: QtWidgets.QLayout):
    for i in reversed(range(layout.count())):
        item = layout.itemAt(i)
        if isinstance(item, QtWidgets.QLayout):
            clearLayout(item)
        elif item.widget():
            item.widget().setParent(None)
        else:
            layout.removeItem(item)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接