Очистить все виджеты в макете в 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)