Clearing Out a Layout



  • Hello-

    I am adding several widgets to a layout using pointers and the new command. Mainly, labels, comboBoxs and lineEdits is what I'm using. What I want to do is clear all the widgets out of the layout.

    I found this code in the help file, however I can't get it to work.
    @ QLayoutItem *child;
    while ((child = layout->takeAt(0)) != 0) {
    ...
    delete child;
    }@

    Can anyone point me in a good direction. From reading the help files, it looks like I should be able this.

    Here's the code I'm using to add the widgets to the layout if that helps.

    @
    QLabel* Something= new QLabel;
    ui->verticalLayout->addWidget(Something);
    Something->show();
    const QString text="Testing";
    Something->setText(text);
    @

    -Thanks



  • I got it figured out, although I don't know if its the best way though.

    These are the missing lines of codes I added to make it work:

    @ QWidget* stepchild;
    stepchild=child->widget;
    delete stepchild;
    @

    Yay, I figured something out!



  • Hello-

    I am having problems with deleting layouts. I am dynamically adding layouts to another layout. I can add and delete them. The problem is in the layout i am adding I have a label and a text box. When I delete the layouts that contain the lineedit and label, they still show and you can type in them. It's all dynamic so I can just tell it to "hide". My understanding is when you delete a QObject it deletes its children too. I don't know if its a bug or I need to do something else.

    Also, if I add the items individually, I can delete everything but it don't look as neat.

    Thanks,
    Arukas



  • I have been playing around with the code. When I delete the layouts, it looks like everything is being deleted. The UI does not recognize, although you can still type in them, which is strange. I tried moving the minimizing, hide/show, repaint, update but the labels and lineedits don't disappear.

    -Thanks



  • try to hide them using QWidget::hide() or QWidget::show(). I was surprised Qt layout system considers the widgets' visibility and update them on the fly.

    Put your QLineEdit and QLabel in a QGroupBox, and just just call those two function on this group box widget in a toggle'd way when pusing a button. I believe you'll get the right result.



  • Using hide and show takes to much away dynamics of the gui. I don't mean to sound ungreatful but a groupboxes aren't a part of my GUI and I don't want to add anything that shouldn't be there in the first place.

    I'm thinking this is just a bug, with the way it behaves.



  • Oh now I understand your goal. But, AFAIK, the QLayout instance is not actually parent of its 'concepturally' children QWidget. It is the layout's parent widget who is the parent of the label and text edit widget you created. So till you delete the layout's parent, any of childrend widget actually won't be gone.

    @
    // dyndel is derived from QWidget
    dyndel::dyndel(QWidget parent, Qt::WFlags flags)
    : QWidget(parent, flags)
    {
    m_label1 = new QLabel(tr("Label1"));
    m_edit1 = new QLineEdit;
    m_label2 = new QLabel(tr("Label2"));
    m_edit2 = new QLineEdit;
    QPushButton
    button = new QPushButton(tr("test"));
    connect(button, SIGNAL(clicked()),
    this, SLOT(removeLayout2()));

    QHBoxLayout* h1 = new QHBoxLayout;
    h1->addWidget(m_label1);
    h1->addWidget(m_edit1);
    m_layout1 = h1;

    QHBoxLayout* h2 = new QHBoxLayout;
    h2->addWidget(m_label2);
    h2->addWidget(m_edit2);
    m_layout2 = h2;

    QVBoxLayout* v = new QVBoxLayout;
    v->addLayout(h1);
    v->addLayout(h2);
    v->addWidget(button);
    setLayout(v);
    }

    dyndel::~dyndel()
    {
    }

    void dyndel::removeLayout2(void)
    {
    delete m_layout2; // oops.
    }
    @

    should be like following, I think.

    @
    #include "dyndel.h"

    #include <QPushButton>
    #include <QVBoxLayout>
    #include <QHBoxLayout>
    #include <QLabel>
    #include <QLineEdit>

    dyndel::dyndel(QWidget parent, Qt::WFlags flags)
    : QWidget(parent, flags)
    {
    m_label1 = new QLabel(tr("Label1"));
    m_edit1 = new QLineEdit;
    m_label2 = new QLabel(tr("Label2"));
    m_edit2 = new QLineEdit;
    QPushButton
    button = new QPushButton(tr("test"));

    connect(button, SIGNAL(clicked()),
    this, SLOT(removeLayout2()));

    QHBoxLayout* h1 = new QHBoxLayout;
    h1->addWidget(m_label1);
    h1->addWidget(m_edit1);
    m_layout1 = h1;

    QHBoxLayout* h2 = new QHBoxLayout;
    h2->addWidget(m_label2);
    h2->addWidget(m_edit2);
    m_layout2 = h2;

    m_layout2Hodler = new QWidget;
    m_layout2Hodler->setLayout(h2);

    QVBoxLayout* v = new QVBoxLayout;
    v->addLayout(h1);
    v->addWidget(m_layout2Hodler);
    v->addWidget(button);
    setLayout(v);
    }

    dyndel::~dyndel()
    {
    }

    void dyndel::removeLayout2(void)
    {
    //delete m_layout2;
    delete m_layout2Hodler;
    }
    @



  • I think I understand what you are saying, but I'm not quite following you. In your code you make reparent the layout manager to a QWidget? The layouts don't inherit from QWidget, however they inherit from QObject. And from my understanding, QObjects delete their children when they are deleted. Are the LineEdits and Labels not children of the layout? Do I need to make a QWidget parent for the layout manager?



  • In above first example, you see we're deleting a 'm_layout2' in slot 'removeLayout2()'. But this does NOT delete its 'conceptually' children QWidget's instances BUT DELETE QLayoutItem which is actually managed by QLayout. In short, QLayout is not part of hierachy in parent/child widget as far as i know.
    (QLayout just help its widgets under interest be the children of the layout's widget).



  • I didn't try your code out, but I did what you did, or at least I think you did. You created a QWidget,m_layout2Hodler, and added the desired layout to it and its the m_layout2Hodler you added not the actual layout. Then when you want the layout gone you delete the QWidget.

    I implemented that in my code, and I couldn't get it to work. Still the exact same problem.



  • Here's some of my code I'm using, if that helps.

    @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(addComboLine()));
    connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(removeAll()));

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::addComboLine()
    {
    QLabel *label = new QLabel;
    QLineEdit *lineEdit = new QLineEdit;
    QHBoxLayout hLayout = new QHBoxLayout;
    QWidget
    stupidKid = new QWidget;

    label->setText("Hello!");
    hLayout->addWidget(label);
    hLayout->addWidget(lineEdit);
    stupidKid->setLayout(hLayout);
    ui->verticalLayout->addWidget(stupidKid);
    

    }

    void MainWindow::removeAll()
    {
    QLayoutItem* child;
    child=ui->verticalLayout->takeAt(0);
    while(child != 0)
    {
    ui->verticalLayout->removeWidget(child->widget());
    delete child;
    child=ui->verticalLayout->takeAt(0);
    }

    }@



  • I did some playing around and I just hide the widget first. If I don't hide the widget first, it totally destroys my GUI. Or should I use a @delete child->widget();@

    Here's my Code:
    @void MainWindow::addComboLine()
    {
    QWidget* stupidKid = new QWidget;
    QLabel *label = new QLabel;
    QLineEdit *lineEdit = new QLineEdit;
    QHBoxLayout *hLayout = new QHBoxLayout;

    label->setText("Hello!");
    hLayout->addWidget(label);
    hLayout->addWidget(lineEdit);
    stupidKid->setLayout(hLayout);
    ui->verticalLayout->addWidget(stupidKid);
    

    }

    void MainWindow::removeAll()
    {
    QLayoutItem* child;
    child=ui->verticalLayout->takeAt(0);
    while(child != 0)
    {
    if(child->widget()!=0)
    child->widget()->hide();
    ui->verticalLayout->removeWidget(child->widget());
    delete child;
    child=ui->verticalLayout->takeAt(0);
    //ui->verticalLayout->update();
    }
    }
    @



  • The first code snippet is fine, works as expected and should be used to clear layouts.

    The problem is the second snippet. You call <code>show()</code> on a widget that is embedded within an layout, which means it is shown as a top-level window as soon as it is removed from the layout. This is not how layouts are meant to be used.

    A layout takes several child widgets and is then set on a widget using QWidget::setLayout(), which is then shown.
    @
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(new QLabel("Label 1"));
    layout->addWidget(new QLabel("Label 2"));

    QWidget* widget = new QWidget;
    widget->setLayout(layout);
    widget->show();
    @
    In your case it is most probably <code>show()</code> or <code>this->show()</code>, not <code>Something->show()</code>.

    Be aware that QLayoutItem::widget() might not neccessarily return a valid widget. So your third snippet doesn't break immediately for the simple fact that <code>delete 0</code> is allowed in C++. [quote]
    If this item is a QWidget, it is returned as a QWidget; otherwise 0 is returned. This function provides type-safe casting.[/quote]
    In addition, this snippet leaks memory, because you just delete the widget the QLayoutItem* <code>child</code> points to and not the QLayoutItem itself, which is dangling from now on because it has been taken out of the layouts child collection.

    EDIT As this discussion has been merged this post relates to post #1 and #2 (which was the actual discussion).



  • Then.. What I meant was....

    @
    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);

    // assume put all your ui inside a plain vanilla QWidget as a
    // simple placeholder for childrend widgets
    //
    // for example, in QtDesginer,
    // 1) you cut all your widgets
    // 2) add a plain vanilla QWidget (named 'placeHolder' whatever)
    // 3) click horizontally layout to make this new widget cover whole client area
    // 4) paste the cut'd widgets inside of this new widget
    // 5) now, the old 'verticalLayout' is the layout of 'placeHolder' widget
    // (was layout of MainWindow I think, though I cannot check your ui file)

    connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(addComboLine()));
    connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(removeAll()));

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::addComboLine()
    {
    QLabel *label = new QLabel;
    QLineEdit *lineEdit = new QLineEdit;
    QHBoxLayout hLayout = new QHBoxLayout;
    QWidget
    stupidKid = new QWidget;

    label->setText("Hello!");
    hLayout->addWidget(label);
    hLayout->addWidget(lineEdit);
    stupidKid->setLayout(hLayout);

    ui->verticalLayout->addWidget(stupidKid);
    }

    void MainWindow::removeAll()
    {
    // QLayoutItem* child;
    // child=ui->verticalLayout->takeAt(0);
    // while(child != 0)
    // {
    // ui->verticalLayout->removeWidget(child->widget());
    // delete child;
    // child=ui->verticalLayout->takeAt(0);
    // }
    delete ui->placeHolder;
    ui->placeHolder = 0;
    }
    @

    though I have to say i've not tested above code yet.



  • There is no need for a placeholder widget just to add a layout to another layout. All layouts have in addition to addWidget() an addLayout() method which allows for directly adding a layout.

    An exception to this rule is QStackedLayout and QFormLayout, but it doesn't make sense adding layouts instead of widgets to them anyways. However, those layouts can be of course added freely to any other layout.

    @
    void MainWindow::addComboLine()
    {
    QHBoxLayout* layout = new QHBoxLayout;

    layout->addWidget(new QLabel("Hello!"));
    layout->addWidget(new QLineEdit);
    
    ui->verticalLayout->addLayout(layout);
    

    }

    void MainWindow::remove(QLayout* layout)
    {
    QLayoutItem* child;
    while((child = layout->takeAt(0)) != 0)
    {
    if(child->layout() != 0)
    {
    remove(child->layout());
    }
    else if(child->widget() != 0)
    {
    delete child->widget();
    }

        delete child;
    }
    

    }

    void MainWindow::removeAll()
    {
    remove(ui->verticalLayout);
    }
    @
    Brain to terminal. Not tested. Exemplary.

    EDIT If you add layouts instead of widgets to a layout, it's children are of course layouts too. So the code needed a bit recursivity to work properly. It indeed removes the items from the layout without recursion, but as widgets placed in layouts have the layouts parent widget as parent (and not the layout itself) the widgets are not deleted along with the layout.



  • Yes you're right, maybe no need to use a place holder widget. But I thought
    @
    delete placeHolder;
    @

    is much simpler, clearer and readable than followings, IMHO

    @
    QLayoutItem* child;
    while ((child = ui->verticalLayout->takeAt(0)) != 0)
    {
    delete child;
    }
    @



  • I don't agree. I tried many things, and I eventually removed the show. "Here's a link to some example code.":http://dl.dropbox.com/u/481280/layoutDeleter.zip It doesn't matter that I use a QWidget or not, the layout is still not deleted from the GUI.



    • If a widget is deleted, all of its children are deleted which is the same as a child is deleted, when its parent is deleted.
    • If a widget A has a layout, and you add widget B to this layout, the layouts parent, A, becomes the new parent for the widget B, not the layout itself.

    This means in your case, that if you add a widget <code>stupidKid</code> to <code>ui->verticalLayout</code>, <code>ui->verticalLayoutWidget</code> (which was implicitly created by the UI designer to hold <code>verticalLayout</code>, as every layout needs a parent widget) becomes the parent of <code>stupidKid</code>, and <code>ui->verticalLayout</code> becomes the layout manager.

    QLayout::takeAt() does not return the widgets themselves, it returns a QLayoutItem, which was used by the QVBoxLayout layout manager to manage the widget. If you now delete the QLayoutItem you do not delete the widget as well, because the layout or the QLayoutItem is not the parent of the widget, the layouts parent widget <code>ui->verticalLayoutWidget</code> is.

    Thus your <code>stupidKit</code> widgets still have a valid parent, thus they have not been deleted and thus they are still shown - they just got their layout manager removed. So for your code to work properly, you will have to delete the widget as well (do not just hide them, this will leak memory).
    @
    void MainWindow::removeAll()
    {
    QLayoutItem* child;
    child=ui->verticalLayout->takeAt(0);
    while(child != 0)
    {
    if(child->widget())
    delete child->widget();

        delete child;
        child=ui->verticalLayout->takeAt(0);
    }
    

    }
    @
    Be aware that this implemention is limited to layouts which contain only widgets. If you add layouts too, you will have to use a recursive implementation, as shown in my previous post.



  • Lets see if I understand you correctly. My confusion is that ui->verticalLayoutWidget is the actually parent widget, which is the source of my problems.

    I got up too early and read your post, and I missed the recursive part. I'll give it a try and see if I can get it to work.

    -Thanks



  • [quote author="Arukas" date="1326991322"]Lets see if I understand you correctly. My confusion is that ui->verticalLayoutWidget is the actually parent widget, which is the source of my problems. [/quote]

    Yes, if you add a widget to a layout, the layouts parent becomes the parent for the widget, <code>ui->verticalLayoutWidget</code> in your case.

    If you want to dig deeper on your own go to KDABs website and fetch "GammaRay":http://www.kdab.com/gammaray, an absolutely great tool when it comes to introspecting and debugging Qt applications.



  • [quote author="Arukas" date="1326976934"]I don't agree. I tried many things, and I eventually removed the show. "Here's a link to some example code.":http://dl.dropbox.com/u/481280/layoutDeleter.zip It doesn't matter that I use a QWidget or not, the layout is still not deleted from the GUI.
    [/quote]

    I inspected your code, and that's not actually what I meant.

    please check "this":http://dl.dropbox.com/u/3699382/layoutDeleter2.zip
    and be noted that any layout instance will be deleted as soon as place holder widget is gone.

    And see the difference and wanna hear your review on it.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.