QPushButton
中只有一个QWidget
,click
该按钮应打开另一个QWidget
,如下所示:
project.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = untitled
TEMPLATE = app
CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}
INCLUDEPATH += ..
SOURCES += ../main.cpp\
../widget.cpp \
../secondwidget.cpp
HEADERS += ../widget.h \
../secondwidget.h
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QVBoxLayout>
#include <QPushButton>
#include "secondwidget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(400, 300);
QVBoxLayout *vLayout = new QVBoxLayout(this);
QPushButton *bt = new QPushButton("Open 2nd Widget");
vLayout->addWidget(bt, 1, Qt::AlignRight);
SecondWidget *secondWidget = new SecondWidget();
// SecondWidget *secondWidget = new SecondWidget(this);
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
Widget::~Widget()
{
}
secondwidget.h
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QWidget>
class SecondWidget : public QWidget
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
signals:
public slots:
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
SecondWidget::SecondWidget(QWidget *parent) : QWidget(parent)
{
setAttribute( Qt::WA_QuitOnClose, false );
}
为this
构造函数传递SecondWidget
(如在注释行中)将在某个时刻制造我的逻辑。因此,当单击按钮时,SecondWidget
不再显示。
发生了什么事?
答案 0 :(得分:2)
除了析构函数和构造函数的问题(以及由于第二个而导致的内存泄漏),此处的项目文件是您可能想要了解的一些内容,以便了解整个父级情况:
您不需要通过this
。分配父级的目的是为了简化QObject
实例的清理过程(包括从QObject
类继承的所有类)。 Qt父子模型遵循C ++标准,析构函数以构造函数的相反顺序调用。简单来说,这意味着以下内容:
想象一下,您创建小部件A,B和C 。使用父属性,您将 B 和 C 指定为 A 的子级。所以我们在这里有创造的顺序:
现在在某些时候你想破坏你的小部件(例如:关闭应用程序)。标准的C ++方法是在下面(与构造相反)顺序销毁它们:
如果我们先找到父母,那确实会发生这种情况。但是我们在这里处理Qt所以我们必须考虑库提供的另一个功能 - 插槽和信号。
每当QObject
被摧毁时,它就会发出一个信号。根据对象在父子关系中扮演的角色,结果是以下之一:
QObject
被销毁 - 这是在我们销毁其父级之前儿童被销毁时发生的事情QObject
的所有孩子首先被销毁,然后销毁 QObject
本身 - 这当父母被摧毁时会发生什么。发出的信号被所有孩子捕获,反过来也被消除了。但是,分配父母不是强制性的。您可以自己手动清理。阅读Qt documentation中有关父子模型的更多信息。
除了所有所有权转移(小部件成为另一个小部件的子级)之外,通常会自动发生,因此不必明确指定小部件的父级。再次从Qt documentation中取出一个例外。如果您将小部件添加到布局,则所有权不会转移到布局本身,而是转移到它所属的QWidget
。
最后有一个重要的案例,如果不指定父母会让事情变得非常非常不同。在这里,我将在QObject
上引用Qt documentation:
将parent设置为0可构造一个没有父对象的对象。 如果对象是窗口小部件,它将成为顶级窗口。
因此,如果您有QWidget
并且没有将其添加到某个布局(并间接添加到具有该布局的QWidget
),例如它将自动成为一个单独的窗口。< / p>
修改强>
检查构造函数,特别是使用secondWidget
对象的方式。正如我上面提到的,如果您不想将其分配给父窗口小部件,需要负责清理。
您为它动态分配内存
SecondWidget *secondWidget = new SecondWidget();
甚至将其连接到您的按钮
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
然而,您永远不会使用delete
释放已分配的内存或将其分配给父窗口小部件,它将自动处理它。因此,您创建了内存泄漏。通过信号和广告位连接QObject
与转让所有权无关。
我个人认为您确实希望secondWidget
成为屏幕上显示的额外窗口。在这种情况下,您需要创建类型为SecondWidget
SecondWidget *secondWidget;
然后在构造函数中分配它并连接您想要的任何插槽和信号
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//...
secondWidget = new SecondWidget();
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
最后释放构造函数中的内存:
Widget::~Widget()
{
delete secondWidget;
}
否则正如我所说,你基本上是在创建对内存块的引用,并且在你离开构造函数之后,引用会因为超出范围而被销毁。
编辑2:
如果您希望secondWidget
作为主窗口小部件的子项,可以使用以下示例:
<强>的main.cpp 强>
#include "widget.h"
#include <QApplication>
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 <QWidget>
#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 <QObject>
#include <QDialog>
class SecondWidget : public QDialog
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
~SecondWidget();
};
#endif // SECONDWIDGET_H
<强> secondwidget.cpp 强>
#include "secondwidget.h"
#include <QFormLayout>
#include <QLabel>
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()
{
}
请注意setParent(parent)
构造函数中的SecondWidget
。您必须调用超类构造函数(如您所做)或从主窗口小部件的构造函数中手动调用setParent(parent). If you don't do that your
secondWidget will 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-&gt; setParent(this)`以设置父节点。
以下是检查父子层次结构是否正常的方法:
对于您拥有的QObject
QWidget
,QLayout
等都是QObject
的所有子类,使用QObject::setObjectName("some name")
分配对象名称
在两个构造函数的末尾添加:
for(int i = 0; i < this->children().count(); i++)
std::cout << this->children()[i]->objectName().toStdString() << std::endl; // Add #include <iostream> to get the output
基本上遍历this
(Widget
或SecondWidget
)的所有孩子,并显示其子女。在我的情况下,我得到了
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
我启动了我的申请。
编辑3:啊,我没有注意到你在调用QWidget(parent)
构造函数中的SecondWidget
。这也可以解决问题,因此您不需要setParent(parent)
。我改变了我的第二个编辑。
答案 1 :(得分:1)
这通常发生在
答案 2 :(得分:1)
实际错误?
需要使用cmd提示符或终端
确保析构函数不会调用自身,这样就改变了执行的补救措施。