Omar Murcia
Omar Murcia

Reputation: 567

How to prevent crash after close QMainWindow in Qt?

I have an error, just after closing my application, I get an error message saying that it has stopped unexpectedly. I reached this:

enter image description here

But, after I close the window, I get this error:

The program has unexpectedly finished. The process was ended forcefully.

My project structure is:

enter image description here

And my code is:

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <windows/login.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Login w;
    w.show();

    return a.exec();
}

util_window.h

#ifndef UTIL_H
#define UTIL_H

#include <QObject>
#include <QMainWindow>

class UtilWindow : QObject
{
public:
    // vars

    // methods
    util();
    void setCenterWindow(QMainWindow* w);
};

#endif // UTIL_H

util_window.cpp

#include "util_window.h"
#include <QDesktopWidget>

UtilWindow::util()
{

}

void UtilWindow::setCenterWindow(QMainWindow *w) {
    int width = w->frameGeometry().width();
    int height = w->frameGeometry().height();
    QDesktopWidget wid;
    int screenWidth = wid.screen()->width();
    int screenHeight = wid.screen()->height();
    w->setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
    w->show();
}

login.h

#ifndef LOGIN_H
#define LOGIN_H

#include <QMainWindow>
#include <QObject>
#include <QWidget>
#include "util/util_window.h"
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QFormLayout>

class Login : public QMainWindow
{
    Q_OBJECT
public:
    // vars
    UtilWindow* util;
    // widgets
    // labels
    QLabel* lblHost;
    QLabel* lblPort;
    QLabel* lblUser;
    QLabel* lblPass;
    // textbox
    QLineEdit* leHost;
    QLineEdit* lePass;
    QLineEdit* leUser;
    QLineEdit* lePort;
    // layouts
    QFormLayout* layout;
    QWidget* central;
    QVBoxLayout* mainLayout;
    QGroupBox* gbCredentials;
    // methods
    void initLabels();
    void initTextBoxes();
    void initUI();
    explicit Login(QWidget *parent = nullptr);
    ~Login();

signals:

public slots:
};

#endif // LOGIN_H

login.cpp

#include "login.h"

Login::Login(QWidget *parent) : QMainWindow(parent)
{
    this->util = new UtilWindow();
    this->initUI();

}

Login::~Login() {
    delete this->lblHost;
    delete this->lblPass;
    delete this->lblPort;
    delete this->lblUser;
    delete this->leHost;
    delete this->lePass;
    delete this->lePort;
    delete this->leUser;
    delete this->layout;
    delete this->central;
    delete this->mainLayout;
    delete this->gbCredentials;
    delete this->util;
}

void Login::initUI() {
    this->setFixedSize(400,400);
    this->setWindowTitle(tr("Inicio de sesión"));
    this->util->setCenterWindow(this);
    this->initLabels();
    this->initTextBoxes();
    this->layout = new QFormLayout();
    this->layout->addRow(this->lblHost, this->leHost);
    this->layout->addRow(this->lblPort, this->lePort);
    this->layout->addRow(this->lblUser, this->leUser);
    this->layout->addRow(this->lblPass, this->lePass);
    this->gbCredentials = new QGroupBox();
    this->gbCredentials->setTitle(tr("Datos de conexión"));
    this->gbCredentials->setLayout(layout);
    this->mainLayout = new QVBoxLayout();
    this->mainLayout->addWidget(gbCredentials);
    this->central = new QWidget();
    this->central->setParent(this);
    this->central->setLayout(this->mainLayout);
    this->setCentralWidget(this->central);
}

void Login::initLabels() {
    this->lblHost = new QLabel();
    this->lblPass = new QLabel();
    this->lblPort = new QLabel();
    this->lblUser = new QLabel();
    this->lblHost->setText(tr("Host: "));
    this->lblPass->setText(tr("Contraseña: "));
    this->lblPort->setText(tr("Puerto: "));
    this->lblUser->setText(tr("Usuario: "));
}

void Login::initTextBoxes() {
    this->leHost = new QLineEdit();
    this->lePass = new QLineEdit();
    this->lePort = new QLineEdit();
    this->leUser = new QLineEdit();
    this->leHost->setPlaceholderText(tr("Host de MySQL"));
    this->lePass->setPlaceholderText(tr("Ingrese su contraseña"));
    this->leUser->setPlaceholderText(tr("Ingrese su nombre de usuario"));
    this->lePort->setPlaceholderText(tr("Ingrese el número de puerto"));
    this->leHost->setToolTip(this->leHost->placeholderText());
    this->leUser->setToolTip(this->leUser->placeholderText());
    this->lePass->setToolTip(this->lePass->placeholderText());
    this->lePort->setToolTip(this->lePort->placeholderText());
}

Thanks in advance!

Upvotes: 2

Views: 2965

Answers (2)

You have deleting QMainWindow elements by yourself :

delete this->lblHost;

When your QObject inherited objects are created on heap with parent, you should not delete it by yourselves. because QObject tree will take care of the memory memory management.

So twice deleting would be happen.

Upvotes: 0

p-a-o-l-o
p-a-o-l-o

Reputation: 10047

When you add widgets to a layout, e.g.

this->layout.addRow(&(this->lblHost), &(this->leHost));

you're parenting them to the layout's widget, in this case your Login main window, and when the parent widget destructor will be called, all children widget will be delete'd. What happens in your code is: the children (line edits and labels) are not instantiated with new, so calling delete on them will crash your application. You should replace the children declarations in your widget header with pointers, this way:

QLabel * lblHost;

// ...

QLineEdit * leHost;

// etc

and instantiate them before adding them to the layout:

this->lblHost = new QLabel();
this->leHost = new QLineEdit();
this->layout.addRow(this->lblHost, this->leHost);
//etc

This applies to all layouts and widgets that have a parent (i.e. central Qwidget).

Also: explicitly calling delete on children is not needed, because the parent destructor will take care of that, and this applies to the central widget as well:

QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time

As thuga points out in comments, there is nothing inherently wrong in instantiating children on the stack, as long as their destructor is called before their parent's, thus they are automatically removed from the parent's children list and the parent destructor will not call delete on them.

As explained here, this is legit:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget widget;
    QLabel label(&widget);
    widget.show();

    return a.exec();
}

because label will go out of scope before widget.

Changing the two objects creation order will cause the application crash at exit time:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QLabel label;
    QWidget widget;

    label.setParent(&widget);

    widget.show();

    return a.exec();
}

Upvotes: 7

Related Questions