Qt - удалить все виджеты из макета? - PullRequest
55 голосов
/ 25 ноября 2010

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

header

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 ]

40 голосов
/ 08 мая 2011

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

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

Когда я хочу отобразить уровень игры, я создаю экземпляр QGridLayout, в который я добавляю QLabels, а затем устанавливаю их растровые изображения для определенных изображений (растровые изображения с прозрачнымчасти).Первый уровень отображается нормально, но при переключении на второй уровень растровые изображения с первого уровня все еще можно было увидеть за новыми (где растровое изображение было прозрачным).

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

35 голосов
/ 16 августа 2011

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

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

Мой вывод: Виджеты необходимо уничтожать вручную или уничтожая родительский WIDGET, а не макет

Виджеты в макете являются дочерними элементами виджета, на котором макет установлен, а не самого макета. Виджеты могут иметь только другие виджеты как родительские, а не макеты.

Мой вывод: То же, что и выше

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

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

// 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;
    }
}
10 голосов
/ 02 марта 2016

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

qDeleteAll(yourWidget->children());

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

void qDeleteAll (ForwardIterator begin, ForwardIterator end)

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

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

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

8 голосов
/ 25 ноября 2010

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

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

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

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

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

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

4 голосов
/ 25 ноября 2010

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

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

QLayoutItem* child;
while ((child = pclLayout->takeAt(0)) != 0)
{
    if (child->widget() != NULL)
    {
        delete (child->widget());
    }
    delete child;
}
2 голосов
/ 28 августа 2013

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

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

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

2 голосов
/ 06 декабря 2010

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

1 голос
/ 15 июня 2011

У меня был похожий случай, когда у меня есть 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;
}
0 голосов
/ 21 октября 2016

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

...