QPushButton中有一个QWidgetclick按钮应打开另一个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不再显示。

这是怎么回事?

最佳答案

除了析构函数和构造函数的问题(以及由于第二个问题而导致的内存泄漏)以及此处的项目文件,您还可能需要了解一些事情,以了解整个父级情况:

您不需要通过this。分配父对象的目的是为了简化QObject实例(包括从QObject类继承的所有类)的清理过程。 Qt父子模型遵循C ++标准,即按析构函数的相反顺序调用析构函数。简单来说,这意味着:

想象一下,您创建了小部件A,B和C。使用父属性,您将B和C分配为A的子代。因此,这里有创建的顺序:


家长)
B,C(儿童)


现在,您需要销毁小部件(例如:关闭应用程序)。标准的C ++方法是按照以下(与构造相反)的顺序销毁它们:


B,C(儿童)
家长)


如果我们先去找父母,这确实会发生。但是,我们在这里处理Qt,因此我们必须考虑库提供的一项附加功能-插槽和信号。

每当QObject被销毁时,它都会发出信号。根据对象在父子关系中扮演的角色,结果为以下之一:


只有被销毁的QObject被销毁-这是在我们销毁其父级之前销毁一个孩子时发生的情况
QObject的所有子级都首先被破坏,接着是QObject本身的破坏-这是父级被破坏时发生的情况。所发射的信号被其所有子级捕获,作为回报,它们也被消除。


但是,分配父母不是必须的。您可以自己手动进行清理。在Qt documentation中了解有关父子模型的更多信息。

除了所有权转移之外(窗口小部件成为另一个窗口小部件的子代)通常会自动发生,因此无需显式指定窗口小部件的父级。再次从Qt documentation中取出内容,这是一个例外。如果将窗口小部件添加到布局,则所有权不会转移到布局本身,而是转移到它的QWidget中。

最后,有一种重要的情况是,不分配父母会使事情变得非常非常不同。在这里,我将引用QObject上的Qt documentation


  将parent设置为0会构造一个没有父对象的对象。如果对象是窗口小部件,它将成为顶级窗口。


因此,例如,如果您有一个QWidget而不将其添加到某个布局(并间接添加到具有该布局的QWidget中),它将自动成为一个单独的窗口。



编辑:
检查构造函数,尤其是检查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-> setParent(this)`来设置父级。

您可以通过以下方法检查父子层次结构是否正常:


为每个拥有的QObjectQWidgetQLayout等都是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


它基本上遍历thisWidgetSecondWidget)的所有子级并显示其子级。就我而言

    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)。我已经更改了第二个编辑。

10-07 12:29