Qt – rimuovi tutti i widget dal layout?

Questo non sembra facile. Fondamentalmente, aggiungo QPushButtons attraverso una funzione a un layout, e quando la funzione viene eseguita, voglio prima cancellare il layout (rimuovendo tutti i QPushButtons e qualsiasi altra cosa ci sia dentro), perché più pulsanti vengono semplicemente aggiunti alla scrollview.

intestazione

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

Ho avuto lo stesso problema: ho un’app di gioco la cui class della finestra principale eredita QMainWindow. Il suo costruttore sembra in parte come questo:

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

Quando voglio mostrare un livello del gioco, istanzio un QGridLayout, nel quale aggiungo QLabels, e quindi imposta le loro pixmap a determinate immagini (pixmap con parti trasparenti). Il primo livello viene visualizzato correttamente, ma quando si passa al secondo livello, le pixmap del primo livello possono ancora essere viste dietro quelle nuove (in cui la pixmap era trasparente).

Ho provato diverse cose per eliminare i vecchi widget. (a) Ho provato a eliminare QGridLayout e creare un’istantanea di una nuova, ma poi ho imparato che l’eliminazione di un layout non elimina i widget aggiunti. (b) Ho provato a chiamare QLabel :: clear () sulle nuove pixmaps, ma ovviamente ciò ha avuto solo un effetto su quelle nuove, non su quelle degli zombi. (c) Ho persino provato a cancellare la mia m_view e m_scene, e ricostruirli ogni volta che ho mostrato un nuovo livello, ma ancora senza fortuna.

Quindi (d) ho provato una delle soluzioni fornite sopra, vale a dire

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

ma quello non ha funzionato, neanche.

Tuttavia, googling ulteriormente, ho trovato una risposta che ha funzionato . Quello che mancava da (d) era una chiamata per delete item->widget() . Quanto segue ora funziona per me:

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

e quindi istanzio un nuovo QGridLayout come con il primo livello, aggiungo i nuovi widget del livello ad esso, ecc.

Qt è fantastico in molti modi, ma penso che questo problema dimostri che le cose potrebbero essere un po ‘più semplici qui.

La pagina di gestione del layout nella guida di Qt afferma:

Il layout riparerà automaticamente i widget (usando QWidget :: setParent ()) in modo che siano figli del widget su cui è installato il layout.

La mia conclusione: i widget devono essere distrutti manualmente o distruggendo il WIDGET padre, non il layout

I widget in un layout sono figli del widget su cui è installato il layout, non del layout stesso. I widget possono avere solo altri widget come genitore, non come layout.

La mia conclusione: come sopra

A @Muelner per “contraddizione” “La proprietà dell’object viene trasferita al layout ed è responsabilità del layout eliminarla.” – Questo non significa WIDGET, ma ITEM che è riparato al layout e verrà eliminato in seguito dal layout. I widget sono ancora figli del widget su cui è installato il layout e devono essere rimossi manualmente o eliminando l’intero widget principale.

Se uno ha davvero bisogno di rimuovere tutti i widget e gli oggetti da un layout, lasciandolo completamente vuoto, ha bisogno di fare una funzione ricorsiva come questa:

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

So che questa domanda è obsoleta e ha risposto, ma: Poiché QtAlgorithms offre qDeleteAll, è ansible eliminare un layout, inclusa la cancellazione di tutti i suoi figli con un one-liner. Questo codice cancella il layout, tutti i suoi figli e tutto ciò che è all’interno del layout ‘scompare’.

 qDeleteAll(yourWidget->children()); 

Ecco la descrizione della funzione sovraccaricata:

void qDeleteAll (ForwardIterator begin, ForwardIterator end)

Elimina tutti gli elementi nell’intervallo [begin, end] utilizzando l’operatore C ++ delete>. Il tipo di elemento deve essere un tipo di puntatore (ad esempio, QWidget *).

Si noti che qDeleteAll deve essere alimentato con un contenitore da quel widget (non quel layout). E nota che qDeleteAll NON elimina yourWidget , ma solo i suoi figli.

Ora è ansible impostare un nuovo layout.

Non sperimentato: perché non creare un nuovo layout, scambiarlo con il vecchio layout ed eliminare il vecchio layout? Questo dovrebbe eliminare tutti gli oggetti che erano di proprietà del layout e lasciare gli altri.

Modifica : dopo aver studiato i commenti alla mia risposta, la documentazione e le fonti Qt ho trovato una soluzione migliore:

Se hai ancora il supporto Qt3 abilitato, puoi usare QLayout :: deleteAllItems () che è fondamentalmente lo stesso di hint nella documentazione di QLayout :: takeAt:

Il seguente frammento di codice mostra un modo sicuro per rimuovere tutti gli elementi da un layout:

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

Modifica : dopo ulteriori ricerche sembra che entrambe le versioni precedenti siano equivalenti: vengono eliminati solo i sottofinestri e i widget senza i genitori. I widget con genitore sono trattati in modo speciale. Sembra che la soluzione di TeL dovrebbe funzionare, devi solo fare attenzione a non eliminare nessun widget di primo livello. Un altro modo sarebbe utilizzare la gerarchia dei widget per eliminare i widget: creare un widget speciale senza genitore e creare tutti i widget rimovibili come figlio di questo widget speciale. Dopo aver pulito il layout, elimina questo widget speciale.

Vuoi anche assicurarti di rimuovere gli spaziatori e le cose che non sono QWidgets. Se sei sicuro che le uniche cose nel tuo layout sono QWidgets, la risposta precedente va bene. Altrimenti dovresti fare questo:

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

È importante sapere come farlo perché se il layout che si desidera cancellare è parte di un layout più grande, non si vuole distruggere il layout. Volete assicurarvi che il vostro layout mantenga la sua posizione e relazione con il resto della vostra finestra.

Dovresti anche fare attenzione, stai creando un carico di oggetti ogni volta che chiami questo metodo e non vengono ripuliti. In primo luogo, probabilmente dovresti creare QWidget e QScrollArea da qualche altra parte e mantenere una variabile membro che punta a loro come riferimento. Quindi il tuo codice potrebbe apparire in questo modo:

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

Non scrivi di andare dall’altra parte, ma potresti anche semplicemente usare un QStackedWidget e aggiungere due viste a questa, una per ogni disposizione di pulsanti di cui hai bisogno. Flippare tra loro due è un non problema quindi e molto meno rischioso di destreggiarsi tra varie istanze di pulsanti creati dynamicmente

Nessuna delle risposte esistenti ha funzionato nella mia applicazione. Una modifica a Darko Maksimovic sembra funzionare, finora. Ecco qui:

 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 necessario, almeno con la mia gerarchia di widget e layout, ricorrere e cancellare l’ampiezza dei widget.

funziona solo per la mia lista pulsanti, se anche i widget stessi vengono cancellati. altrimenti i vecchi pulsanti sono ancora visibili:

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

Ho avuto un caso simile in cui ho un QVBoxLayout contenente oggetti QHBoxLayout creati dynamicmente contenenti un numero di istanze QWidget . Per qualche motivo non sono riuscito a eliminare i widget eliminando né il QVBoxLayout di livello superiore né i singoli QHBoxLayouts. L’unica soluzione che ho avuto modo di lavorare è stata quella di passare attraverso la gerarchia e rimuovere e cancellare tutto in particolare:

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

Se vuoi rimuovere tutti i widget, puoi fare qualcosa del genere:

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

Se non si fa qualcosa di divertente quando si aggiungono widget a layout e layout ad altri layout, dovrebbero essere tutti riparati in aggiunta al widget principale. Tutti i QObject hanno uno slot deleteLater() che causerà l’ deleteLater() dell’object non appena il controllo viene restituito al ciclo degli eventi. I widget cancellati in questo Manor cancellano anche i loro figli. Pertanto è sufficiente chiamare deleteLater () sull’elemento più alto nell’albero.

in hpp

 QScrollArea * Scroll; 

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

si noti inoltre che nell’esempio _layout è una variabile locale e non la stessa cosa di _layout nel file di intestazione (rimuovere la parte QVBoxLayout* ). Si noti inoltre che i nomi che iniziano con _ sono riservati agli implementatori di librerie standard. Io uso il trailing _ come in var_ per mostrare una variabile locale, ci sono molti gusti ma i precedenti _ e __ sono tecnicamente riservati.

Ho una ansible soluzione per questo problema (vedi Qt: cancella tutti i widget dall’interno di un layout di QWidget ). Elimina tutti i widget e i layout in due passaggi separati.

Passaggio 1: Elimina tutti i widget

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

Passaggio 2: Elimina tutti i layout

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

Dopo il passaggio 2, il MYTOPWIDGET deve essere pulito.