Qt – удалить все виджеты из макета?

Это не кажется легким. В основном, я добавляю QPushButtons через функцию к макету, и когда функция выполняется, я хочу сначала очистить макет (удалив все QPushButtons и все остальное там), потому что больше кнопок просто добавляются к scrollview.

заголовок

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

У меня была та же проблема: у меня есть игровое приложение, чей class главного windows наследует QMainWindow. Его конструктор выглядит примерно так:

 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) Я попытался вызвать QLabel :: clear () в новых pixmaps, но это, конечно, повлияло только на новые, а не на зомби. (c) Я даже пытался удалить мои m_view и m_scene и реконструировать их каждый раз, когда я отображал новый уровень, но до сих пор не повезло.

Тогда (d) я попробовал одно из приведенных выше решений, а именно

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

но это тоже не сработало.

Однако, googling дальше, я нашел ответ, который сработал . То, что отсутствовало в (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 отлично по-разному, но я думаю, что эти проблемы показывают, что здесь может быть немного легче.

Страница управления макетами в справочной системе Qt:

Макет автоматически отобразит виджеты (используя 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; } } 

Я знаю, что этот вопрос старый и ответил, но: Поскольку QtAlgorithms предлагает qDeleteAll, можно удалить макет, включая удаление всех его дочерних элементов с помощью однострочного. Этот код удаляет макет, все его дети и все внутри макета исчезает.

 qDeleteAll(yourWidget->children()); 

Вот описание перегруженной функции:

void qDeleteAll (начало ForwardIterator, конец ForwardIterator)

Удаляет все элементы в диапазоне [начало, конец] с помощью оператора C ++ delete>. Тип элемента должен быть типом указателя (например, QWidget *).

Обратите внимание, что qDeleteAll необходимо загрузить с помощью этого контейнера из этого виджета (не для этого макета). Обратите внимание, что qDeleteAll НЕ удаляет yourWidget – только его дети.

Теперь можно установить новый макет.

Untried: почему бы не создать новый макет, не поменять его старым макетом и удалить старый макет? Это должно удалить все элементы, принадлежащие макету, и оставить остальных.

Редактирование : после изучения комментариев к моему ответу, документации и источников Qt я нашел лучшее решение:

Если вы все еще поддерживаете поддержку Qt3, вы можете использовать QLayout :: deleteAllItems (), который в основном такой же, как подсказка в документации для QLayout :: takeAt:

Следующий fragment кода показывает безопасный способ удаления всех элементов из макета:

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

Изменить : после дальнейшего исследования похоже, что обе версии, приведенные выше, эквивалентны: удаляются только подъязыки и виджет без родителей. Виджеты с родителями рассматриваются особым образом. Похоже, решение TeL должно работать, вы должны быть осторожны, чтобы не удалять виджеты верхнего уровня. Другим способом было бы использовать иерархию виджетов для удаления виджетов: создать специальный виджет без родителя и создать все ваши удаляемые виджеты в качестве дочернего элемента этого специального виджета. После очистки макета удалите этот специальный виджет.

Вы также хотите убедиться, что вы удаляете проставки и вещи, которые не являются QWidgets. Если вы уверены, что единственными вещами в вашем макете являются QWidgets, предыдущий ответ в порядке. В противном случае вы должны сделать следующее:

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

Важно знать, как это сделать, потому что если макет, который вы хотите очистить, является частью более крупного макета, вы не хотите уничтожать макет. Вы хотите, чтобы ваш макет поддерживал его место и отношение к остальной части вашего windows.

Вы также должны быть осторожны, вы создаете нагрузку объектов каждый раз, когда вы вызываете этот метод, и они не очищаются. Во-первых, вы, вероятно, должны создать 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); 

Вы не пишете о том, как идти другим путем, но вы также можете просто использовать QStackedWidget и добавить к нему два вида, по одному для каждой комбинации кнопок, которые вам нужны. Переворот между ними – это не проблема, и гораздо меньше риска, чем манипуляция различными примерами динамически создаваемых кнопок

Ни один из существующих ответов не работал в моем приложении. Модификация 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; } } 

Было необходимо, по крайней мере, с моей иерархией виджетов и макетов, рекурсивно и удалить виджеты.

работает только для моего кнопочного списка, если элементы виджета также удалены. в противном случае старые кнопки все еще видны:

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

У меня был аналогичный случай, когда у меня есть 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; } 

Если вы хотите удалить все виджеты, вы можете сделать что-то вроде этого:

 foreach (QObject *object, _layout->children()) { QWidget *widget = qobject_cast(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 – это локальная переменная, а не _layout в файле заголовка (удалите часть QVBoxLayout* ). Также обратите внимание, что имена, начинающиеся с _, зарезервированы для разработчиков стандартной библиотеки. Я использую trailing _ как в var_ для отображения локальной переменной, есть много вкусов, но предшествующие _ и __ являются технически зарезервированными.

У меня есть возможное решение этой проблемы (см. 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 должен быть чистым.

Interesting Posts
Давайте будем гением компьютера.