Qt: ¿quita todos los widgets del diseño?

Esto no parece fácil. Básicamente, agrego QPushButtons a través de una función a un diseño, y cuando la función se ejecuta, quiero borrar el diseño primero (eliminando todos los QPushButtons y cualquier otra cosa que esté allí), porque más botones solo se anexan a la vista de desplazamiento.

encabezamiento

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

Tuve el mismo problema: tengo una aplicación de juego cuya clase de ventana principal hereda QMainWindow. Su constructor se ve en parte así:

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

Cuando quiero mostrar un nivel del juego, instancia un QGridLayout, en el cual agrego QLabels, y luego establezco sus pixmaps en ciertas imágenes (pixmaps con partes transparentes). El primer nivel se muestra bien, pero al cambiar al segundo nivel, los pixmaps del primer nivel aún se podían ver detrás de los nuevos (donde el mapa de píxeles era transparente).

Intenté varias cosas para eliminar los viejos widgets. (a) Intenté eliminar QGridLayout y crear una instancia de uno nuevo, pero luego descubrí que eliminar un diseño no elimina los widgets que se le agregaron. (b) Intenté llamar a QLabel :: clear () en los nuevos pixmaps, pero eso solo tuvo un efecto en los nuevos, no en los zombis. (c) Incluso traté de borrar mi m_view y m_scene, y reconstruirlos cada vez que mostraba un nuevo nivel, pero todavía no tuve suerte.

Luego (d) intenté una de las soluciones dadas anteriormente, a saber

 QLayoutItem *wItem; while (wItem = widget->layout()->takeAt(0) != 0) delete wItem; 

pero eso tampoco funcionó.

Sin embargo, buscando en Google aún más, encontré una respuesta que funcionó . Lo que faltaba en (d) era una llamada para delete item->widget() . Lo siguiente ahora me funciona:

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

y luego ejemplifico un nuevo QGridLayout como con el primer nivel, le agrego los widgets del nuevo nivel, etc.

Qt es genial en muchos sentidos, pero creo que este problema muestra que las cosas podrían ser un poco más fáciles aquí.

Página de administración de diseño en los estados de ayuda de Qt:

El diseño reparará automáticamente los widgets (utilizando QWidget :: setParent ()) para que sean elementos secundarios del widget en el que está instalado el diseño.

Mi conclusión: los widgets deben ser destruidos manualmente o destruyendo el WIDGET padre, no el diseño

Los widgets en un diseño son elementos secundarios del widget en el que está instalado el diseño, no del diseño en sí. Los widgets solo pueden tener otros widgets como padres, no diseños.

Mi conclusión: igual que arriba

A @Muelner por “contradicción” “La propiedad del artículo se transfiere al diseño, y es responsabilidad del diseño eliminarlo”. – Esto no significa WIDGET, sino ITEM que se vuelve a representar en el diseño y luego se eliminará mediante el diseño. Los widgets siguen siendo elementos secundarios del widget en el que está instalado el diseño, y deben eliminarse manualmente o eliminando el widget principal.

Si uno realmente necesita eliminar todos los widgets y elementos de un diseño, dejándolo completamente vacío, necesita hacer una función recursiva como esta:

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

Sé que esta pregunta es antigua y respondida, pero dado que QtAlgorithms ofrece qDeleteAll, es posible eliminar un diseño, incluida la eliminación de todos sus elementos secundarios con un diseño único. Este código elimina el diseño, todos sus elementos secundarios y todo lo que está dentro del diseño “desaparece”.

 qDeleteAll(yourWidget->children()); 

Aquí está la descripción de la función sobrecargada:

void qDeleteAll (ForwardIterator begin, ForwardIterator end)

Elimina todos los elementos en el rango [begin, end] usando el operador C ++ delete>. El tipo de elemento debe ser un tipo de puntero (por ejemplo, QWidget *).

Tenga en cuenta que qDeleteAll necesita alimentarse con un contenedor de ese widget (no ese diseño). Y tenga en cuenta que qDeleteAll NO elimina su yourWidget , solo sus hijos.

Ahora se puede establecer un nuevo diseño.

No probado: ¿por qué no crear un nuevo diseño, cambiarlo por el diseño anterior y eliminar el diseño anterior? Esto debería eliminar todos los elementos que fueron propiedad del diseño y dejar los otros.

Editar : Después de estudiar los comentarios a mi respuesta, la documentación y las fonts de Qt encontré una mejor solución:

Si todavía tiene habilitada la compatibilidad con Qt3, puede usar QLayout :: deleteAllItems (), que es básicamente lo mismo que la sugerencia en la documentación de QLayout :: takeAt:

El siguiente fragmento de código muestra una forma segura de eliminar todos los elementos de un diseño:

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

Editar : después de más investigaciones, parece que ambas versiones anteriores son equivalentes: solo se eliminan las subcajas y el widget sin padres. Los widgets con los padres se tratan de una manera especial. Parece que la solución de TeL debería funcionar, solo debes tener cuidado de no eliminar ningún widgets de nivel superior. Otra forma sería usar la jerarquía de widgets para eliminar widgets: cree un widget especial sin padre y cree todos los widgets extraíbles como elemento secundario de este widget especial. Después de limpiar el diseño, elimine este widget especial.

También quiere asegurarse de eliminar los espaciadores y elementos que no son QWidgets. Si está seguro de que las únicas cosas en su diseño son QWidgets, la respuesta anterior está bien. De lo contrario, deberías hacer esto:

 QLayoutItem *wItem; while (wItem = widget->layout()->takeAt(0) != 0) delete wItem; 

Es importante saber cómo hacerlo porque si el diseño que desea borrar forma parte de un diseño más grande, no desea destruir el diseño. Desea asegurarse de que su diseño mantenga su lugar y relación con el rest de su ventana.

También debe tener cuidado, está creando una carga de objetos cada vez que llama a este método, y no se limpian. En primer lugar, probablemente debería crear QWidget y QScrollArea en otro lugar, y mantener una variable miembro apuntando a ellos como referencia. Entonces tu código podría verse más o menos así:

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

No escribes sobre ir por el otro lado, pero también puedes usar un QStackedWidget y agregar dos vistas a esto, una para cada disposición de botones que necesites. Alternar entre los dos no es un problema entonces y mucho menos riesgo que hacer malabares con varias instancias de botones creados dinámicamente

Ninguna de las respuestas existentes funcionó en mi aplicación. Una modificación a Darko Maksimovic parece funcionar, hasta ahora. Aquí está:

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

Era necesario, al menos con mi jerarquía de widgets y diseños, recurse y eliminar widgets explícitamente.

solo funciona para mi lista de botones, si también se eliminan los widgets themeselves. de lo contrario, los botones viejos siguen visibles:

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

Tuve un caso similar en el que tengo un QVBoxLayout contiene objetos QHBoxLayout creados dinámicamente que contienen varias instancias de QWidget . Por alguna razón, no pude deshacerme de los widgets eliminando ni el nivel superior QVBoxLayout ni el QHBoxLayouts individual. La única solución que obtuve para trabajar fue pasando por la jerarquía y eliminando y eliminando todo específicamente:

 while(!vbox->isEmpty()) { QLayout *hb = vbox->takeAt(0)->layout(); while(!hb->isEmpty()) { QWidget *w = hb->takeAt(0)->widget(); delete w; } delete hb; } 

Si desea eliminar todos los widgets, podría hacer algo como esto:

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

Si no hace nada gracioso al agregar widgets a diseños y diseños a otros diseños, todos deberían volver a crearse al agregarlos a su widget principal. Todos los QObjects tienen un deleteLater() que hará que el objeto se elimine tan pronto como se devuelva el control al bucle de evento. Los widgets eliminados en esta Mansión también eliminan a sus hijos. Por lo tanto, solo necesita llamar a deleteLater () en el elemento más alto en el árbol.

en hpp

 QScrollArea * Scroll; 

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

También tenga en cuenta que en su ejemplo, _layout es una variable local y no es lo mismo que _layout en el archivo de encabezado (elimine la parte QVBoxLayout* ). También tenga en cuenta que los nombres que comienzan con _ están reservados para implementadores de biblioteca estándar. Yo uso el var_ igual que en var_ para mostrar una variable local, hay muchos gustos, pero los _ y _ precedentes son técnicamente reservados.

Tengo una posible solución para este problema (ver Qt – Borrar todos los widgets desde el diseño de un QWidget ). Elimine todos los widgets y diseños en dos pasos separados.

Paso 1: eliminar todos los widgets

  QList< QWidget* > children; do { children = MYTOPWIDGET->findChildren< QWidget* >(); if ( children.count() == 0 ) break; delete children.at( 0 ); } while ( true ); 

Paso 2: borra todos los diseños

  if ( MYTOPWIDGET->layout() ) { QLayoutItem* p_item; while ( ( p_item = MYTOPWIDGET->layout()->takeAt( 0 ) ) != nullptr ) delete p_item; delete MYTOPWIDGET->layout(); } 

Después del paso 2, su MYTOPWIDGET debe estar limpio.