Очистить все виджеты в макете в Pyqt

Есть ли способ очистить (удалить) все виджеты в макете?

self.plot_layout = QtGui.QGridLayout()
self.plot_layout.setGeometry(QtCore.QRect(200,200,200,200))
self.root_layout.addLayout(self.plot_layout)
self.plot_layout.addWidget(MyWidget())

Теперь я хочу заменить виджет в plot_layout с новым виджетом. Есть ли простой способ очистить все виджеты в plot_layout? Я не вижу такого способа.

14 ответов

Решение

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

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().setParent(None)

Документация говорит о QWidget:

Новый виджет удаляется при удалении его родителя.

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

Чтобы проверить и подтвердить, что макет пуст:

for i in range(layout.count()): print i

Кажется, есть другой способ сделать это. Вместо использования функции setParent используйте функцию deleteLater(), например:

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().deleteLater()

В документации сказано, что QObject.deleteLater (self)

Планирует этот объект для удаления.

Однако, если вы запустите тестовый код, указанный выше, он напечатает некоторые значения. Это указывает на то, что в макете все еще есть элементы, в отличие от кода с setParent.

Это может быть слишком поздно, но я просто хотел добавить это для дальнейшего использования:

def clearLayout(layout):
  while layout.count():
    child = layout.takeAt(0)
    if child.widget():
      child.widget().deleteLater()

Адаптировано из Qt docs http://doc.qt.io/qt-5/qlayout.html. Помните, что когда вы удаляете дочерние элементы из макета в цикле while или for, вы эффективно изменяете индекс # каждого дочернего элемента в макете. Вот почему вы столкнетесь с проблемами при использовании for i in range() петля.

Ответ от PALEN работает хорошо, если вам не нужно добавлять новые виджеты в ваш макет.

for i in reversed(range(layout.count())): 
    layout.itemAt(i).widget().setParent(None)

Но в какой-то момент вы получите "Ошибка сегментации (ядро сброшено)", если вы очистите и заполните макет много раз или со многими виджетами. Кажется, что макет хранит список виджетов и что этот список ограничен в размере.

Если вы удалите виджеты таким образом:

for i in reversed(range(layout.count())): 
    widgetToRemove = layout.itemAt(i).widget()
    # remove it from the layout list
    layout.removeWidget(widgetToRemove)
    # remove it from the gui
    widgetToRemove.setParent(None)

Вы не получите эту проблему.

Вот как я очищаю макет:

def clearLayout(layout):
    if layout != None:
        while layout.count():
            child = layout.takeAt(0)
            if child.widget() is not None:
                child.widget().deleteLater()
            elif child.layout() is not None:
                clearLayout(child.layout())

Вы можете использовать close() метод widget:

for i in range(layout.count()): layout.itemAt(i).widget().close()

Я использую:

    while layout.count() > 0: 
        layout.itemAt(0).setParent(None)

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

def clearLayout(layout):
print("-- -- input layout: "+str(layout))
for i in reversed(range(layout.count())):
    layoutItem = layout.itemAt(i)
    if layoutItem.widget() is not None:
        widgetToRemove = layoutItem.widget()
        print("found widget: " + str(widgetToRemove))
        widgetToRemove.setParent(None)
        layout.removeWidget(widgetToRemove)
    elif layoutItem.spacerItem() is not None:
        print("found spacer: " + str(layoutItem.spacerItem()))
    else:
        layoutToRemove = layout.itemAt(i)
        print("-- found Layout: "+str(layoutToRemove))
        clearLayout(layoutToRemove)

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

Мое решение этой проблемы - переопределить метод setLayout QWidget. Следующий код обновляет макет до нового макета, который может содержать или не содержать элементы, которые уже отображаются. Вы можете просто создать новый объект макета, добавить к нему все, что захотите, а затем вызвать setLayout. Конечно, вы также можете просто вызвать clearLayout, чтобы удалить все.

def setLayout(self, layout):
    self.clearLayout()
    QWidget.setLayout(self, layout)

def clearLayout(self):
    if self.layout() is not None:
        old_layout = self.layout()
        for i in reversed(range(old_layout.count())):
            old_layout.itemAt(i).widget().setParent(None)
        import sip
        sip.delete(old_layout)

Из документов:

Чтобы удалить виджет из макета, позвоните removeWidget(), призвание QWidget.hide() на виджете также эффективно удаляет виджет из макета до QWidget.show() называется.

removeWidget наследуется от QLayoutпоэтому его нет в списке QGridLayout методы.

У меня были проблемы с ранее упомянутыми решениями. Были устаревшие виджеты, которые вызывали проблемы; Я подозреваю, что удаление было запланировано, но не завершено. Мне также пришлось установить родительский элемент виджета на None. это было моим решением:

      def clearLayout(layout):
    while layout.count():
        child = layout.takeAt(0)
        childWidget = child.widget()
        if childWidget:
            childWidget.setParent(None)
            childWidget.deleteLater()
      for i in reversed(range(layout.count())):
    if layout.itemAt(i).widget():
        layout.itemAt(i).widget().setParent(None)
    else:
        layout.removeItem(layout.itemAt(i))

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

Если вы хотите заменить все дочерние элементы виджета, то QObject функции findChildren должен получить вас там, например, я не знаю, как функции шаблона обернуты в pyqt, хотя. Но вы также можете искать виджеты по имени, если вы их знаете.

Проблемы возникают, если у вас есть вложенные макеты, но вы пытаетесь удалить только виджеты. Эту проблему можно решить, если вы проверите тип элемента и, если элемент является макетом, вам следует рекурсивно вызвать функцию:

      def clearLayout(layout):
    for i in reversed(range(layout.count())): 
        child = layout.itemAt(i)
        if child.widget() is not None:
            child.widget().deleteLater()
        elif child.layout() is not None:
            clearLayout(child.layout())
        for i in reversed (range(layout.count())):
            layout.itemAt(i).widget().close()
            layout.takeAt(i)

или же

        for i in range(layout.count()):
            layout.itemAt(0).widget().close()
            layout.takeAt(0)
Другие вопросы по тегам