There is one QPushButton
in a QWidget
, click
the button should open another QWidget
, as coded below:
project.pro
In addition to the problems with your destructor and constructor (and the memory leak you have because of the second), and the project file here are a few things that you may want to know in order to understand the whole parent situation:
You don't need to pass this
. The purpose of assigning a parent is to be able to simplify the cleanup procedure for QObject
instances (including all classes inheriting from the QObject
class). Qt parent-child model follows the C++ standard that is destructors are invoked in the reversed order of their constructors. In simple terms this means the following:
Imagine you create widget A, B and C. Using the parent property you assign B and C to be children of A. So here we have the order of creation:
Now at some point you want to destroy your widgets (example: closing the application). The standard C++ way is to destroy them in the following (reversed to the construction) order:
And this is indeed what happens if we go for the parent first. However we are dealing with Qt here so we have to consider one additional feature the library provides - slots and signals.
Whenever a QObject
gets destroyed it emits a signal. Based on the role the object plays in the parent-child relationship the outcome is one of the following:
QObject
that is destroyed gets destroyed - this is what happens when a child gets destroyed before we destroy its parentQObject
get destroyed first followed by the destruction of the QObject
itself - this is what happens when a parent gets destroyed. The emitted signal is caught by all its children which in return also get eliminated.However assigning a parent is not mandatory. You can do the cleanup manually yourself. Read more about the parent-child model in the Qt documentation.
In addition to all that ownership transfer (a widget becomes a child of another widget) often happens automatically so it is not necessary to explicitly specify a widget's parent. Again taking things out of the Qt documentation an exception is made here. If you add a widget to a layout, the ownership is NOT transferred to the layout itself but to the QWidget
it is part of.
Finally there is one important case when not assigning a parent makes things very, very different. Here I shall quote the Qt documentation on QObject
:
Setting parent to 0 constructs an object with no parent. If the object is a widget, it will become a top-level window.
So if you have a QWidget
and don't add it to some layout (and indirectly to the QWidget
that has that layout) for example it will automatically become a separate window.
EDIT:
Check your constructor and in particular the way you work with your secondWidget
object. As I've mentioned above in case you don't want to assign it to a parent widget you need to take care of the cleaning.
You dynamically allocate the memory for it
SecondWidget *secondWidget = new SecondWidget();
and even connect it to your button
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
however you never release the allocated memory using delete
or assigning it to a parent widget, which will take care of it automatically. Hence you create a memory leak. Connecting a QObject
via signals and slots doesn't have anything to do with transfer of ownership.
I personally assume that you actually want a secondWidget
to be an extra window shown on the screen. In this case you need to create a class member of type SecondWidget
SecondWidget *secondWidget;
then allocate it inside your constructor and connect whichever slots and signals you want
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//...
secondWidget = new SecondWidget();
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
and finally release the memory inside your constructor:
Widget::~Widget()
{
delete secondWidget;
}
Otherwise as I said you are basically creating a reference to a memory block and right after you leave your constructor that reference gets destroyed since it runs out of scope.
EDIT 2:
Here is a small example how to do it if you want secondWidget
as a child to your main widget:
main.cpp
#include "widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.hpp
#ifndef WIDGET_H
#define WIDGET_H
#include
#include "secondwidget.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
SecondWidget *secondWidget = new SecondWidget(this); # secondWidget is now officially adopted by Widget
# If you skip the parent assignment inside SecondWidget you can call secondWidget->setParent(this) here
connect(ui->pushButton, SIGNAL(clicked(bool)), secondWidget, SLOT(show()));
}
Widget::~Widget()
{
delete ui;
}
secondwidget.hpp
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include
#include
class SecondWidget : public QDialog
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
~SecondWidget();
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
#include
#include
SecondWidget::SecondWidget(QWidget *parent) : QDialog(parent)
{
# If you don't call the constructor of your superclass you can still assign a parent by invoking setParent(parent) here
QFormLayout *layout = new QFormLayout();
QLabel *label = new QLabel("SecondWidget here");
layout->addWidget(label); # Transfer ownership of label to SecondWidget
setLayout(layout);
}
SecondWidget::~SecondWidget()
{
}
Note the setParent(parent)
inside the SecondWidget
's constructor. You either have to invoke the superclass constructor (as you have done) or manually call setParent(parent). If you don't do that your
secondWidgetwill not be assigned as a child to your main widget where you create it and thus you will produce a memory leak. You can also invoke
secondWidget->setParent(this)` from within the constructor of your main widget in order to set the parent.
Here is how you can check if the parent-child hierarchy is okay:
To each QObject
that you have (QWidget
, QLayout
etc. are all subclasses of QObject
) assign an object name using QObject::setObjectName("some name")
At the end of both of your constructors add:
for(int i = 0; i < this->children().count(); i++)
std::cout << this->children()[i]->objectName().toStdString() << std::endl; // Add #include to get the output
which basically traverses all the children of this
(Widget
or SecondWidget
) and displays its children. In my case I got
label // Printing out children of SecondWidget
formLayout // Printing out children of SecondWidget
gridLayout // Printing out children of Widget
pushButton // Printing out children of Widget
main second widget // Printing out children of Widget
once I launched my application.
EDIT 3: Ah, I didn't notice that you are calling the QWidget(parent)
in you SecondWidget
constructor. This also does the trick so you don't need setParent(parent)
. I have altered my second EDIT.