web-dev-qa-db-fra.com

Qt - supprimer tous les widgets de la mise en page?

Cela ne semble pas facile. Fondamentalement, j'ajoute QPushButtons par le biais d'une fonction à une présentation, et lorsque la fonction s'exécute, je souhaite d'abord effacer la présentation (en supprimant tous les QPushButtons et tout le reste), car plusieurs boutons sont simplement ajoutés au défilement.

entête

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

J'ai eu le même problème: j'ai une application de jeu dont la classe de fenêtre principale hérite de 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 );

Lorsque je veux afficher un niveau du jeu, j'instancie un QGridLayout dans lequel j'ajoute des QLabels, puis j'affine leurs pixmaps à certaines images (pixmaps à parties transparentes). Le premier niveau s’affiche correctement, mais lors du passage au deuxième niveau, les pixmaps du premier niveau étaient toujours visibles derrière les nouveaux (où le pixmap était transparent).

J'ai essayé plusieurs choses pour supprimer les anciens widgets. (a) J'ai essayé de supprimer QGridLayout et d'instancier un nouveau, mais j'ai ensuite appris que la suppression d'une présentation ne supprime pas les widgets qui y ont été ajoutés. (b) J'ai essayé d'appeler QLabel :: clear () sur les nouveaux pixmaps, mais cela n'a évidemment eu d'effet que sur les nouveaux, pas sur les zombies. (c) J'ai même essayé de supprimer mes m_view et m_scene et de les reconstruire à chaque fois que je affichais un nouveau niveau, mais toujours pas de chance.

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

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

mais cela n'a pas fonctionné non plus.

Cependant, googler plus loin, j'ai trouvé une réponse qui a fonctionné . Ce qui manquait dans d) é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();
}

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

C'est formidable à bien des égards, mais je pense que ce problème montre que les choses pourraient être un peu plus faciles ici.

39
Teemu Leisti

Gestion de la mise en page La page de l'aide de Qt indique:

La mise en page reparent automatiquement les widgets (en utilisant QWidget :: setParent ()) afin qu'ils soient les enfants du widget sur lequel la mise en page est installée.

Ma conclusion: Les widgets doivent être détruits manuellement ou en détruisant le WIDGET parent, pas la présentation.

Les widgets d'une mise en page 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 que autres widgets en tant que parent, pas de mises en page.

Ma conclusion: Comme ci-dessus

@Muelner pour "contradiction" "La propriété de l'élément est transférée à la présentation, et c'est à celle-ci qu'il incombe de la supprimer." - cela ne signifie pas WIDGET, mais ITEM qui est réparé à la mise en page et sera supprimé ultérieurement par la mise en page. Les widgets sont toujours les enfants du widget sur lequel la mise en page est installée et ils doivent être supprimés soit manuellement, soit en supprimant tout le widget parent.

S'il faut vraiment supprimer tous les widgets et les éléments d'une mise en page en la laissant complètement vide, il doit créer 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
Darko Maksimovic

Je sais que cette question est ancienne et a répondu à cette question, mais: Etant donné que QtAlgorithms propose qDeleteAll, il est possible de supprimer une mise en page, y compris la suppression de tous ses enfants avec une ligne, tout dans la mise en page "disparaît".

qDeleteAll(yourWidget->children());

Voici la description de la fonction surchargée:

void qDeleteAll (ForwardIterator begin, ForwardIterator end)

Supprime tous les éléments de la plage [begin, end] à l'aide de 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 de ce widget (et non de cette présentation). Et notez que qDeleteAll ne supprime pas yourWidget - uniquement ses enfants.

Maintenant, une nouvelle mise en page peut être définie.

9
m.w.

Untried: Pourquoi ne pas créer une nouvelle mise en page, l'échanger avec l'ancienne mise en page et supprimer l'ancienne? Cela devrait supprimer tous les éléments appartenant à la présentation et laisser les autres.

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

Si le support Qt3 est toujours activé, vous pouvez utiliser QLayout :: deleteAllItems (), qui est fondamentalement le même que conseil dans la documentation de QLayout :: takeAt:

Le fragment de code suivant montre un moyen sûr de supprimer tous les éléments d'une présentation:

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

Modifier : Après des recherches approfondies, il semble que les deux versions ci-dessus soient équivalentes: seuls les sous-projets et le widget sans parents sont supprimés. Les widgets avec parent sont traités de manière particulière. Il semble que la solution de TeL devrait fonctionner, vous devez seulement faire attention à ne pas supprimer les widgets de niveau supérieur. Une autre solution consisterait à utiliser la hiérarchie des widgets pour supprimer les widgets: créez un widget spécial sans parent et créez tous vos widgets amovibles en tant qu'enfants de ce widget spécial. Après avoir nettoyé la disposition, supprimez ce widget spécial.

7
hmuelner

Vous voulez également vous assurer que vous enlevez les espaceurs et les choses qui ne sont pas QWidgets. Si vous êtes certain que QWidgets n’a que des éléments dans votre mise en page, la réponse précédente convient. Sinon, vous devriez faire ceci:

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

Il est important de savoir comment procéder car si la mise en page que vous souhaitez effacer fait partie d'une mise en page plus grande, vous ne voulez pas la détruire. Vous voulez vous assurer que votre mise en page conserve sa place et sa relation avec le reste de votre fenêtre.

Vous devez é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. Premièrement, vous devriez probablement créer QWidget et QScrollArea ailleurs, et conserver une variable membre pointant vers eux pour référence. Ensuite, votre code pourrait ressembler à 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
Liz

Aucune des réponses existantes n'a fonctionné dans mon application. Une modification de celle de Darko Maksimovic semble fonctionner jusqu'à présent. C'est ici:

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 de dispositions, de rediffuser et de supprimer explicitement les widgets.

2
Alex S

Vous n'écrivez pas sur le processus inverse, mais vous pouvez également utiliser une variable QStackedWidget et y ajouter deux vues, une pour chaque disposition de boutons dont vous avez besoin. Basculer entre les deux n’est donc pas un problème et beaucoup moins de risque que de jongler avec différentes instances de boutons créés dynamiquement

2
Harald Scheirich

ne fonctionne que pour ma liste de boutons, si les widgets sont eux-mêmes supprimés. sinon les anciens boutons sont toujours visibles:

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

J'ai eu un cas similaire où j'ai un QVBoxLayout contenant des objets QHBoxLayout créés dynamiquement contenant un nombre d'instances QWidget. Pour une raison quelconque, je ne pouvais pas me débarrasser des widgets en ne supprimant ni le QVBoxLayout de niveau supérieur ni les QHBoxLayouts individuels. La seule solution à laquelle j'ai eu à travailler était de passer par la hiérarchie et de tout supprimer et tout effacer:

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

J'ai une solution possible à ce problème (voir Qt - Efface tous les widgets de l'intérieur de la présentation de QWidget ). Supprimez tous les widgets et mises en page 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
boto

Si vous ne faites rien de drôle en ajoutant des widgets à des mises en page et que des mises en page à d'autres mises en page, elles doivent toutes être réparées lors de l'ajout à leur widget parent. Tous les QObjects ont un emplacement deleteLater() qui supprime l’objet dès que le contrôle est rendu à la boucle d’événements. Les widgets supprimés dans ce manoir suppriment également leurs enfants. Par conséquent, vous devez simplement appeler deleteLater () sur l'élément le plus élevé de l'arborescence. 

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

notez également 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 (supprimez la partie QVBoxLayout*). Notez également que les noms commençant par _ sont réservés aux implémenteurs de bibliothèques standard. J'utilise la fin _ comme dans var_ pour afficher une variable locale, il y a beaucoup de goûts mais les précédants _ et __ sont techniquement réservés.

0
odinthenerd

Si vous souhaitez 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
BastiBen