wake-up-neo.com

Qt - alle Widgets vom Layout entfernen?

Das scheint nicht einfach zu sein. Grundsätzlich füge ich QPushButtons über eine Funktion zu einem Layout hinzu, und wenn die Funktion ausgeführt wird, möchte ich zuerst das Layout löschen (alle QPushButtons und das, was sich sonst noch darin befindet) entfernen, da mehr Schaltflächen an die Bildlaufansicht angehängt werden.

header

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

}
51
user375566

Ich hatte das gleiche Problem: Ich habe eine Spiel-App, deren Hauptfensterklasse QMainWindow erbt. Sein Konstruktor sieht teilweise so aus:

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

Wenn ich ein Level des Spiels anzeigen möchte, instantiiere ich ein QGridLayout, in das ich QLabels einfügt, und setze dann ihre Pixmaps auf bestimmte Bilder (Pixmaps mit transparenten Teilen). Die erste Ebene wird in Ordnung angezeigt. Wenn Sie jedoch zur zweiten Ebene wechseln, werden die Pixmaps der ersten Ebene immer noch hinter den neuen angezeigt (wobei die Pixmap transparent war).

Ich habe verschiedene Dinge versucht, um die alten Widgets zu löschen. (a) Ich habe versucht, QGridLayout zu löschen und ein neues zu instanziieren, habe dann aber erfahren, dass durch das Löschen eines Layouts die hinzugefügten Widgets nicht gelöscht werden. (b) Ich habe versucht, QLabel :: clear () auf den neuen Pixmaps aufzurufen, aber das hatte natürlich nur Auswirkungen auf die neuen, nicht auf die Zombie-Karten. (c) Ich habe sogar versucht, m_view und m_scene zu löschen und jedes Mal neu zu erstellen, wenn ich ein neues Level anzeigte, aber immer noch kein Glück.

Dann (d) habe ich eine der oben genannten Lösungen ausprobiert, nämlich

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

aber das hat auch nicht funktioniert.

Googeln weiter, Ich habe eine Antwort gefunden, die funktionierte . Was bei (d) fehlte, war ein Aufruf an delete item->widget(). Folgendes funktioniert jetzt für mich:

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

und dann instantiiere ich ein neues QGridLayout wie beim ersten Level, füge die Widgets des neuen Levels hinzu usw.

Qt ist in vielerlei Hinsicht großartig, aber ich denke, diese Probleme zeigen, dass es hier etwas einfacher sein könnte.

39
Teemu Leisti

Layoutverwaltung Seite in Qt's Hilfe besagt:

Das Layout wird die Widgets automatisch neu erstellen (mithilfe von QWidget :: setParent ()), sodass sie dem Widget untergeordnet sind, auf dem Das Layout ist installiert.

Meine Schlussfolgerung: Widgets müssen manuell oder durch Zerstörung des übergeordneten WIDGET-Objekts zerstört werden, nicht das Layout

Widgets in einem Layout sind untergeordnete Elemente des Widgets, auf dem die Das Layout ist installiert, nicht das Layout selbst. Widgets können nur .__ haben. andere Widgets als übergeordnete Elemente, keine Layouts.

Mein Fazit: Dasselbe wie oben

An @Muelner für "Widerspruch" "Das Eigentum des Artikels wird auf das Layout übertragen, und es liegt in der Verantwortung des Layouts, es zu löschen." - Dies bedeutet nicht WIDGET, sondern ITEM, der im Layout geändert wurde und später vom Layout gelöscht wird. Widgets sind immer noch Kinder des Widget, auf dem das Layout installiert ist. Sie müssen entweder manuell entfernt werden oder durch Löschen des gesamten übergeordneten Widgets.

Wenn wirklich alle Widgets und Elemente aus einem Layout entfernt und vollständig leer gelassen werden müssen, muss eine rekursive Funktion wie folgt erstellt werden:

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

Ich weiß, dass diese Frage alt und beantwortet ist, aber: Da QtAlgorithms qDeleteAll anbietet, ist es möglich, ein Layout einschließlich der Löschung aller untergeordneten Elemente mit einem Einzeiler zu löschen. Dieser Code löscht das Layout, alle untergeordneten Elemente und Alles im Layout 'verschwindet'.

qDeleteAll(yourWidget->children());

Hier ist die Beschreibung der überladenen Funktion:

void qDeleteAll (ForwardIterator-Beginn, ForwardIterator-Ende)

Löscht alle Elemente im Bereich [Anfang, Ende] mit dem C++ - Operator delete>. Der Elementtyp muss ein Zeigertyp sein (z. B. QWidget *).

Beachten Sie, dass qDeleteAll mit einem Container aus diesem Widget (nicht diesem Layout) gespeist werden muss. Beachten Sie, dass qDeleteAll NICHT yourWidget löscht - nur dessen Kinder.

Jetzt kann ein neues Layout festgelegt werden.

9
m.w.

Untried: Erstellen Sie ein neues Layout, tauschen Sie es gegen das alte Layout aus und löschen Sie das alte Layout. Dies sollte alle Elemente löschen, die sich im Besitz des Layouts befinden, und die anderen Elemente verlassen.

Edit : Nachdem ich die Kommentare zu meiner Antwort, die Dokumentation und die Qt-Quellen gelesen hatte, fand ich eine bessere Lösung:

Wenn Sie noch Qt3-Unterstützung aktiviert haben, können Sie QLayout :: deleteAllItems () verwenden. Dies ist im Wesentlichen der gleiche wie der Hinweis in der Dokumentation zu QLayout :: takeAt:

Das folgende Codefragment zeigt eine sichere Methode zum Entfernen aller Elemente aus einem Layout:

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

Edit : Nach weiteren Recherchen scheinen beide Versionen gleichwertig zu sein: Nur Unterlayouts und Widget ohne Eltern werden gelöscht. Widgets mit übergeordneten Elementen werden auf besondere Weise behandelt. Es sieht so aus, als sollte die Lösung von TeL funktionieren. Sie sollten nur darauf achten, keine Widgets der obersten Ebene zu löschen. Eine andere Möglichkeit wäre, die Widget-Hierarchie zum Löschen von Widgets zu verwenden: Erstellen Sie ein spezielles Widget ohne übergeordnetes Element und erstellen Sie alle entfernbaren Widgets als untergeordnetes Element dieses speziellen Widget. Nach dem Bereinigen des Layouts löschen Sie dieses spezielle Widget.

7
hmuelner

Sie möchten auch sicherstellen, dass Sie Abstandhalter und Elemente entfernen, die keine QWidgets sind. Wenn Sie sicher sind, dass QWidgets nur in Ihrem Layout enthalten sind, ist die vorherige Antwort in Ordnung. Ansonsten sollten Sie folgendes tun:

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

Es ist wichtig zu wissen, wie das geht, denn wenn das zu löschende Layout Teil eines größeren Layouts ist, möchten Sie das Layout nicht zerstören. Sie möchten sicherstellen, dass Ihr Layout seinen Platz behält und in Bezug auf den Rest Ihres Fensters steht.

Sie sollten auch aufpassen, dass Sie bei jedem Aufruf dieser Methode eine Menge von Objekten erstellen und diese nicht bereinigt werden. Erstens sollten Sie QWidget und QScrollArea wahrscheinlich an anderer Stelle erstellen und eine Mitgliedervariable beibehalten, die als Referenz auf sie verweist. Dann könnte Ihr Code ungefähr so ​​aussehen:

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

Keine der vorhandenen Antworten funktionierte in meiner Bewerbung. Eine Modifikation von Darko Maksimovic scheint bisher zu funktionieren. Hier ist es:

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

Es war zumindest bei meiner Hierarchie der Widgets und Layouts notwendig, Widgets explizit wiederzugeben und zu löschen.

2
Alex S

Sie schreiben nicht über den umgekehrten Weg, Sie können aber auch einfach eine QStackedWidget verwenden und zwei Ansichten hinzufügen, eine für jede Anordnung von Schaltflächen, die Sie benötigen. Das Umschalten zwischen den beiden ist dann kein Thema und ein viel geringeres Risiko als das Jonglieren verschiedener Instanzen dynamisch erzeugter Schaltflächen

2

funktioniert nur für meine Schaltflächenliste, wenn auch die Widgets-Themenselves gelöscht werden. Ansonsten sind die alten Buttons noch sichtbar:

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

Ich hatte einen ähnlichen Fall, in dem ich eine QVBoxLayout mit dynamisch erstellten QHBoxLayout-Objekten mit einer Anzahl von QWidget-Instanzen habe. Aus irgendeinem Grund konnte ich die Widgets nicht entfernen, indem ich weder das oberste QVBoxLayout noch die einzelnen QHBoxLayouts löschte. Die einzige Lösung, die ich zur Arbeit bekam, bestand darin, die Hierarchie durchzugehen und alles gezielt zu entfernen und zu löschen:

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

Ich habe eine mögliche Lösung für dieses Problem (siehe Qt - Alle Widgets aus dem QWidget-Layout löschen ) Löschen Sie alle Widgets und Layouts in zwei Schritten.

Schritt 1: Löschen Sie alle Widgets

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

Schritt 2: Löschen Sie alle Layouts

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

Nach Schritt 2 sollte Ihr MYTOPWIDGET sauber sein.

0
boto

Wenn Sie beim Hinzufügen von Widgets zu Layouts und Layouts zu anderen Layouts nichts Lustiges tun, sollten sie alle beim Hinzufügen zum übergeordneten Widget neu erstellt werden. Alle QObjects verfügen über einen deleteLater()-Slot, der bewirkt, dass das Objekt gelöscht wird, sobald die Steuerung wieder in die Ereignisschleife aufgenommen wird. In diesem Manor gelöschte Widgets löschen auch ihre Kinder. Dazu müssen Sie einfach deleteLater () für das höchste Element in der Baumstruktur aufrufen. 

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

beachten Sie auch, dass in Ihrem Beispiel der _layout eine lokale Variable ist und nicht mit dem _layout in der Header-Datei identisch ist (entfernen Sie den QVBoxLayout*-Teil). Beachten Sie auch, dass Namen, die mit _ beginnen, für Standard-Bibliotheksimplementierer reserviert sind. Ich benutze nachstehende _ wie in var_, um eine lokale Variable anzuzeigen, es gibt viele Geschmacksrichtungen, aber die vorangegangenen _ und __ sind technisch reserviert.

0
odinthenerd

Wenn Sie alle Widgets entfernen möchten, können Sie Folgendes tun:

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