Qt-supprimer tous les widgets de layout?

Cela ne semble pas facile. Fondamentalement, J'ajoute des QPushButtons par l'intermédiaire d'une fonction à une layout, et lorsque la fonction s'exécute, Je veux d'abord effacer la layout (en supprimant tous les QPushButtons et tout ce qui s'y trouve), parce que plus de boutons sont juste ajoutés au scrollview.

en-tête

QVBoxLayout* _layout;

rpc

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

}
50
demandé sur eyllanesc 2010-11-25 01:37:09

12 réponses

j'ai eu le même problème: j'ai une application de jeu dont la classe de fenêtre principale hérite QMainWindow. Son constructeur ressemble en partie à ceci:

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

quand je veux afficher un niveau du jeu, j'instancie un QGridLayout, dans lequel J'ajoute des QLabels, puis je règle leurs pixmaps sur certaines photos (pixmaps avec des parties transparentes). Le premier niveau affiche très bien, mais lors du passage au deuxième niveau, les pixmaps du premier niveau pouvaient encore être vus derrière les nouveaux (où le pixmap était transparent).

j'ai essayé plusieurs choses pour supprimer l'ancienne widgets. (a) j'ai essayé de supprimer le QGridLayout et d'instancier un nouveau, mais j'ai ensuite appris que la suppression d'un layout ne supprime pas les widgets ajoutés. (b) j'ai essayé d'appeler QLabel::clear() sur les nouvelles images, mais qui bien sûr n'ont qu'un effet sur les nouveaux, pas le zombie. (c) j'ai même essayé de supprimer mes m_view et m_scene, et de les reconstruire à chaque fois que je affiche un nouveau niveau, mais toujours pas de chance.

, Puis (d) j'ai essayé l'une des solutions proposées ci-dessus, à savoir

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

mais ça n'a pas marché non plus.

cependant, googler plus loin, j'ai trouvé une réponse qui a fonctionné . Ce qui manquait au point d), c'était un appel à delete item->widget() . Ce qui suit fonctionne maintenant pour moi:

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

et puis j'instancie un nouveau QGridLayout comme avec le premier niveau, ajouter les widgets du nouveau niveau, etc.

Qt est grande dans beaucoup de façons, mais je ne pense que c'problèmes montre que les choses pourraient être un peu plus facile ici.

37
répondu Teemu Leisti 2011-05-07 23:36:14

Plan de gestion page de Qt aider les états à:

le layout répartira automatiquement les widgets (en utilisant QWidget:: setParent ()) de sorte qu'ils soient les enfants du widget sur lequel la disposition est installée.

Ma conclusion: Widgets doivent être détruits manuellement ou en détruisant le parent du WIDGET, pas de mise en page

Les Widgets dans un layout sont les enfants du widget sur lequel le layout est installé, Pas de la mise en page elle-même. Les Widgets ne peuvent avoir autres widgets en tant que parent, pas layouts.

Ma conclusion: comme ci-dessus

à @Muelner pour" contradiction "" la propriété de l'article est transférée au layout, et c'est la responsabilité du layout de le supprimer."- cela ne signifie pas WIDGET, mais élément qui est reparenté à la mise en page et sera supprimé plus tard par la mise en page. Les Widgets sont encore des enfants du widget sur lequel le layout est installé, et ils doivent être supprimés soit manuellement, soit en supprimant le widget parent entier.

si on a vraiment besoin de supprimer tous les widgets et les éléments d'un layout, le laissant complètement vide, il a besoin de faire une fonction récursive comme celle-ci:

// 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
répondu Darko Maksimovic 2018-06-05 19:17:11

non testé: pourquoi ne pas créer un nouveau layout, l'échanger avec l'ancien layout et supprimer l'ancien layout? Cela devrait supprimer tous les éléments qui appartenaient à la mise en page et laisser les autres.

Edit : après avoir étudié les commentaires à ma réponse, la documentation et les sources Qt j'ai trouvé une meilleure solution:

si vous avez encore le support Qt3 activé, vous pouvez utiliser QLayout::deleteAllItems () qui est fondamentalement le même que l'indication dans la documentation pour QLayout:: takeAt:

le fragment de code suivant montre une façon sûre de supprimer tous les éléments d'une mise en page:

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

Edit : après de plus amples recherches, il semble que les deux versions ci-dessus sont équivalentes: seuls les sous-niveaux et widget sans parents sont supprimés. Les Widgets avec parent sont traités d'une manière spéciale. Il semble que la solution de TeL devrait fonctionner, vous devez seulement être prudent ne pas supprimer de widgets de haut niveau. Une autre façon serait d'utiliser la hiérarchie des widgets pour supprimer les widgets: créer un widget spécial sans parent et créer tous vos widgets amovibles comme enfant de ce widget spécial. Après avoir nettoyé le layout, supprimez ce widget spécial.

7
répondu hmuelner 2011-07-01 16:28:20

je sais que cette question Est vieille et a répondu, mais: depuis QtAlgorithms offre qDeleteAll, il est possible de supprimer un layout, y compris la suppression de tous ses enfants avec une seule doublure. Ce code supprime la disposition, tous ses enfants et tout ce qui est à l'intérieur de la disposition "disparaît".

qDeleteAll(yourWidget->children());

Voici la description de la fonction surchargée:

nul qDeleteAll(ForwardIterator commencer, ForwardIterator fin)

supprime tous les éléments dans la gamme [begin, end] en utilisant l'opérateur c++ delete>. Le type d'élément doit être un type de pointeur (par exemple, QWidget *).

notez que qDeleteAll doit être alimenté avec un conteneur à partir de ce widget (pas cette disposition). Et notez que qDeleteAll ne supprime pas yourWidget - seulement ses enfants.

maintenant un nouveau layout peut être défini.

7
répondu m.w. 2017-11-16 10:28:48

vous voulez également vous assurer que vous retirez les entretoises et les choses qui ne sont pas des QWidgets. Si vous êtes sûr que les seules choses dans votre mise en page sont QWidgets, la réponse précédente est très bien. Sinon, vous devriez faire ceci:

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

il est important de savoir comment faire car si la mise en page que vous voulez effacer fait partie d'une mise en page plus grande, vous ne voulez pas détruire la mise en page. Vous voulez vous assurer que votre mise en page maintient sa place et le reste de votre fenêtre.

vous devriez également faire attention, vous créez une charge d'objets à chaque fois que vous appelez cette méthode, et ils ne sont pas nettoyés. Tout d'abord, vous devriez probablement créer QWidget et QScrollArea ailleurs, et garder une variable membre pointant vers eux pour référence. Alors votre code pourrait ressembler à quelque chose comme ceci:

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
répondu Liz 2010-11-25 15:35:00

vous ne parlez pas d'aller dans l'autre sens, mais vous pouvez également utiliser un QStackedWidget et ajouter deux vues à cela, une pour chaque arrangement de boutons dont vous avez besoin. Le retournement entre les deux est alors un non-problème et beaucoup moins de risque que de jongler avec diverses instances de boutons créés dynamiquement

2
répondu Harald Scheirich 2010-12-06 18:16:20

aucune des réponses existantes n'a fonctionné dans ma demande. Une modification à Darko Maksimovic semble fonctionner, jusqu'à présent. Le voici:

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

il était nécessaire, au moins avec ma hiérarchie de widgets et layouts, de revenir et de supprimer widgets explicity.

2
répondu Alex S 2013-08-28 19:10:59

ne fonctionne que pour ma buttonlist, si les widgets themeselves sont aussi supprimés. sinon, les vieux boutons sont encore visibles:

QLayoutItem* child;
while ((child = pclLayout->takeAt(0)) != 0)
{
    if (child->widget() != NULL)
    {
        delete (child->widget());
    }
    delete child;
}
2
répondu Martin Wilde 2014-01-29 14:42:43

j'ai eu un cas similaire où j'ai eu un QVBoxLayout contenant dynamiquement créé QHBoxLayout objets contenant un certain nombre de QWidget instances. Pour une raison quelconque, Je ne pouvais pas me débarrasser des widgets soit en supprimant ni le niveau supérieur QVBoxLayout ou les QHBoxLayouts individuels. La seule solution que j'ai trouvée pour travailler était de passer par la hiérarchie et de supprimer et supprimer tout spécifiquement:

while(!vbox->isEmpty()) {
    QLayout *hb = vbox->takeAt(0)->layout();
    while(!hb->isEmpty()) {
        QWidget *w = hb->takeAt(0)->widget();
        delete w;
    }
    delete hb;
}
1
répondu teukkam 2011-06-15 09:32:39

si vous voulez supprimer tous les widgets, vous pouvez faire quelque chose comme ceci:

foreach (QObject *object, _layout->children()) {
  QWidget *widget = qobject_cast<QWidget*>(object);
  if (widget) {
    delete widget;
  }
}
0
répondu BastiBen 2010-11-24 22:48:45

si vous ne faites rien de drôle lorsque vous ajoutez des widgets à des layouts et des layouts à d'autres layouts, ils doivent tous être reparentés lors de l'ajout à leur widget parent. Tous les QObjects ont un slot deleteLater() qui fera que L'objet sera supprimé dès que le contrôle sera retourné à la boucle d'événement. Les Widgets supprimés dans ce manoir suppriment aussi leurs enfants. Par conséquent, il vous suffit d'appeler deleteLater() sur l'élément le plus élevé de l'arbre.

en hpp

QScrollArea * Scroll;

rpc

void ClearAndLoad(){
    Scroll->widget()->deleteLater();
    auto newLayout = new QVBoxLayout;
    //add buttons to new layout
    auto widget = new QWidget;
    widget->setLayout(newLayout);
    Scroll->setWidget(widget);
}

notez aussi que dans votre exemple, le _layout est une variable locale et pas la même chose que le _layout dans le fichier d'en-tête (supprimer le QVBoxLayout* partie). Notez également que les noms commençant par _ sont réservés aux développeurs de bibliothèques standards. J'utilise le suivant _ comme dans var_ pour montrer une variable locale, il y a beaucoup de goûts, mais les précédents _ et __ sont techniquement réservés.

0
répondu odinthenerd 2013-12-02 17:40:23

j'ai une solution possible pour ce problème (voir Qt - effacer tous les widgets de L'intérieur de la mise en page D'un QWidget ). Supprimer tous les widgets et layouts en deux étapes distinctes.

Étape 1: Supprimer tous les widgets

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

Étape 2: Supprimer toutes les mises en page

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

après l'étape 2, votre MYTOPWIDGET doit être propre.

0
répondu boto 2017-05-23 11:55:01