Подтвердить что ты не робот

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

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

заголовок

QVBoxLayout* _layout;

каст

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

}
4b9b3361

Ответ 1

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

Ответ 2

Страница "Управление макетами" в справке Qt:

Макет автоматически отобразит виджеты (используя QWidget:: setParent()), чтобы они были дочерними элементами виджета, на которых макет установлен. Мой вывод: Виджеты должны быть уничтожены вручную или путем уничтожения родительского WIDGET, а не макета

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

В @Muelner для "противоречия" "Право собственности на элемент переносится в макет, и его макет несет ответственность за его удаление". - это не означает WIDGET, но ITEM, который повторно представлен в макете и будет удален позже макетом. Виджеты по-прежнему являются дочерними элементами виджета, на котором установлен макет, и их нужно удалить вручную или удалив весь родительский виджет.

Если действительно нужно удалить все виджеты и элементы из макета, оставив его полностью пустым, ему нужно сделать рекурсивную функцию следующим образом://неглубоко проверено, похоже, работает, но применяет логику

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

qDeleteAll(yourWidget->children());

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

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

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

Ни один из существующих ответов не работал в моем приложении. До сих пор действует модификация Дарко Максимовича. Вот он:

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

Если вы не делаете ничего смешного при добавлении виджетов к макетам и макетам в другие макеты, все они должны быть переработаны при добавлении к их родительскому виджету. Все 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_, чтобы показать локальную переменную, есть много вкусов, но предшествующие _ и __ технически зарезервированы.

Ответ 12

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