Opass
Opass

Reputation: 79

In QT, how to remove child widget from parent after receiving the signal generated by child?

In QT, how to remove child widget from parent after receiving the signal generated by child?

I am using QT 5.7 and write a simple program with 2 widget, view1 and view2. There is a button named "btn_1" inside view1, and a button named "btn_leave" inside view2.

When you click "btn_1", it will generate a view2 object and add it into layout of view1. I want view2 be deleted when clicking "btn_leave".

Here is my thought. When clicking leave_btn in view2, it emits a signal called "leave". And I connect this signal to a lambda function in order to delete view2.

void VIEW1::on_btn_1_clicked() {
    VIEW2 *view2 = new VIEW2();
    layout->addWidget(view2);

    connect(view2, &VIEW2::leave, this, [&view2]() {
        delete view2;
    });
}

The program goes crashed not surprisingly, because I delete view2 while signal is emitting from view2. view2 may access its member after emitting leave signal.

So I rewrite it with deleteLater. According to QT document, the object will be deleted when control returns to the event loop.

void VIEW1::on_btn_1_clicked() {
    VIEW2 *view2 = new VIEW2();
    layout->addWidget(view2);

    connect(view2, &VIEW2::leave, this, [&view2]() {
        view2.deleteLater();
    });
}

But surprisingly, the program goes crashed again. Is there anything I misunderstand about the usage of deleteLater or there are still some event inside event queue access view2 after calling deletelater()?

I have uploaded my whole program(created with QT creator) to github if that helps.

Upvotes: 1

Views: 1128

Answers (1)

eyllanesc
eyllanesc

Reputation: 243983

You have to pass the pointer of view2, not the pointer of the pointer, besides this it is not necessary.

void VIEW1::on_btn_1_clicked() {
    VIEW2 *view2 = new VIEW2();
    layout->addWidget(view2);

    connect(view2, &VIEW2::leave, [view2]() {
        view2->deleteLater();
    });
}

Or simply do not use a lambda function, but the new connection style:

void VIEW1::on_btn_1_clicked() {
    VIEW2 *view2 = new VIEW2();
    layout->addWidget(view2);
    connect(view2, &VIEW2::leave, view2, &VIEW2::deleteLater);
}

Another option is not to create the leave signal, and make the connection of the clicked signal

VIEW2::VIEW2(QWidget *parent) :
    QWidget(parent)
{
    label = new QLabel(this);
    label->setText("view2");
    btn_leave = new QPushButton(this);
    btn_leave->setText("leave");

    layout = new QHBoxLayout;
    layout->addWidget(label);
    layout->addWidget(btn_leave);
    setLayout(layout);

    connect(btn_leave, &QAbstractButton::clicked, this, &VIEW2::deleteLater);
}

Upvotes: 4

Related Questions