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

Очистить все виджеты в макете в 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? Я не вижу такого метода.

4b9b3361

Ответ 1

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

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.

Ответ 2

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

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#takeAt. Помните, что когда вы удаляете дочерние элементы из макета в цикле while или for, вы эффективно изменяете индекС# каждого дочернего элемента в макете. Вот почему вы столкнетесь с проблемами при использовании цикла for i in range().

Ответ 3

Ответ от 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)

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

Мое решение этой проблемы - переопределить метод 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)

Ответ 7

Я использую:

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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)

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

Ответ 11

        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)