PDA

View Full Version : Calling destructors on close event



gyre
27th November 2007, 05:36
Hi...I have a question about calling constructors on close events...
I have implemented a close event for my MainWindow plus I set an attribute of mainwindow that sets it to delete itself when closed:

setAttribute(Qt::WA_DeleteOnClose);

But....when I create some widgets with this mainwindow as their parent they wont delete themselves when they are displayed at the time when the mainwindow is closed. Yes they get closed with the mainwindow but I think theyre not deleted.... I dont understand that since it should deleteOnClose as set in that attribute I mentioned and with its delete it should delete all of its child widgets...
Am I right or wrong ?
Problem is that my app doesnt exit when I close it at the time when some of its child widgets were not closed yet and are displayed with it. When I close the app MainWindow the process is still running in the background and it does not finish running...Mainwindow gets closed but stays running in a background process....so I assumed that it has to do something with not deleting of child widgets that are displayed when closing its parents....
Does anyone know how to fix this ?
Thanks in advance...

DeepDiver
27th November 2007, 07:37
Can you please post some code. It'll be easier for us to analyse.

Good luck,

Tom

wysota
27th November 2007, 10:53
The application gets closed when the last window gets closed (regardless of whether it is destroyed or not). So until at least one top-level window remains visible, your application won't autoclose itself. And if some widgets remain visible when you close the main window, it means they are not children of the main window (you didn't pass a pointer to the main window as the parent agument to their constructor).

gyre
27th November 2007, 14:45
I said THEY DONT REMAIN VISIBLE....they get closed with the main window BUT the process of application is still running even after I close it...
But this happens ONLY when I close main window when some of its child widgets are displayed...If they are not displayed when closing the mainwindow the prcess of app gets finished with the close correctly...


void MainWindow::on_actionViewSentMsgHistory_triggered( )
{
qDebug("DISPLAY MESSAGE HISTORY TABLE VIEW");
OpenHistoryView *sentMsgHistoryView = new OpenHistoryView(this);
sentMsgHistoryView->historyTableView->setModel(sentMsgHistory);
sentMsgHistoryView->show();
}

void MainWindow::on_sendMultiMessage_triggered()
{
qDebug("DISPLAY MULTIMESSAGE DIALOG AND SEND");
MsgSeqWidget *widget = new MsgSeqWidget(this);
widget->show();
}

those are ONLY TWO child widgets...

headers of those widgets:


class OpenHistoryView : public QMainWindow, public Ui::historyView
{
Q_OBJECT

public:
OpenHistoryView(QMainWindow *parent = NULL);
~OpenHistoryView();
void contextMenuEvent(QContextMenuEvent *event);

QAction *sendAction;

public slots:
void on_cancelButton_clicked();
void on_sendButton_clicked();
void on_removeSelectedButton_clicked();
void on_removeAllButton_clicked();

void sendMessages();
};

class MsgSeqWidget : public QMainWindow , public Ui::MsgSeqEditor
{
Q_OBJECT
public:
MsgSeqWidget(QMainWindow *parent= NULL);
~MsgSeqWidget();
QVcaMsgSeqModel *model;
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
signals:
void msgEnqueue(const QVcaCanMsg &canmsg);
public slots:
void on_addMsgButton_clicked();
void on_cancelButton_clicked();
void on_removeMsgButton_clicked();
void on_sendMsgButton_clicked();
};


and constructors:


OpenHistoryView::OpenHistoryView(QMainWindow *parent)
: QMainWindow(parent)
{
setupUi(this);
setWindowTitle("Sent Message History View");
historyTableView->setSortingEnabled(true);
sendAction = new QAction(tr("&Send"), this);
connect(sendAction, SIGNAL(triggered()),
this, SLOT(sendMessages()));
setAttribute(Qt::WA_DeleteOnClose);
}

MsgSeqWidget::MsgSeqWidget(QMainWindow *parent)
: QMainWindow(parent)
{
setupUi(this);
setWindowTitle("Send Message Sequence Tool");
model = new QVcaMsgSeqModel(this);
msgTableView->setModel(model);
msgTableView->setSortingEnabled(true);
msgTableView->setColumnWidth(2, 180);
setAttribute(Qt::WA_DeleteOnClose);
connect(this, SIGNAL(msgEnqueue(const QVcaCanMsg&)),
model, SLOT(messageEnqueued(const QVcaCanMsg&)));
}

wysota
27th November 2007, 14:54
and what about main() and MainWindow? As I said, the fact that a widget gets deleted or not is meaningless. The application reacts to widgets being closed, not destroyed. You might be blocking the event loop somehow.

gyre
27th November 2007, 15:05
main:


int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(mystyle);

QApplication app(argc, argv);
MainWindow *mainWindow;

QCoreApplication::setOrganizationName("CTU");
QCoreApplication::setOrganizationDomain("www.cvut.cz");
QCoreApplication::setApplicationName("qcanalyzer");

vca_debug_flg |= VCA_DEB_MSGDATA | VCA_DEB_RAWMSG;

if (argc != 2)
{
mainWindow = new MainWindow;//load default settings when no settings file specified
} else {
mainWindow = new MainWindow(QCoreApplication::arguments().at(1));//load settings from specified filename
}

mainWindow->show();
return app.exec();
}


MainWindow header:


class MainWindow : public QMainWindow, private Ui::mainWindow
{
Q_OBJECT

public:
MainWindow(QMainWindow *parent = NULL);
MainWindow(QString settingsFileNamePath, QMainWindow *parent = NULL);
~MainWindow();

QVcaCanMsgModel* getModel(){
return msgTableModel;
}

QVcaCanMsgModel* getMsgHistoryModel(){
return sentMsgHistory;
}

AppSettings* getSettings(){
return appSettings;
}

QVcaNet* getNetworkIfc(){
return networkIfc;
}

void setClientConnected(bool _clientConnected){
clientConnected = _clientConnected;
}

bool clientIsConnected(){
return clientConnected;
}

bool saveFile(const QString &fileName);

protected:
void closeEvent(QCloseEvent *event);
void contextMenuEvent(QContextMenuEvent *event);
bool maybeSaved();
private:
bool clientConnected;//WHEN MONITORTHREAD BECOMES A MEMBER OF MAINWINDOW THIS WILL NO MORE MAKE SENSE - WILL BE ASKING EITHER IF THREAD IS RUNNING OR IF IT WAS CREATED-IT CAN BE DELETED WITH PAUSE...

AppSettings *appSettings;
QVcaCanMsgModel *msgTableModel;
QVcaNet *networkIfc;
AnalyzerMonitorThread *monitorThread;

QVcaCanMsgModel *sentMsgHistory;

signals:
void msgHistoryUpdate(const QVcaCanMsg &canmsg);

public slots:
void on_actionAbout_triggered();
void on_actionConfigure_triggered();
void on_actionAttachToInterface_triggered();
void on_actionDetachInterface_triggered();
void on_roughMsgCheckBox_stateChanged();
void on_viewStatsCheckBox_stateChanged();
void on_sendButton_clicked();
void on_periodicButton_clicked();
void on_clearButton_clicked();
void on_followLogHeadCheckBox_stateChanged();
void unsetFollowHeadCheckBox(int state);

void on_actionOpenLogFile_triggered();
void changeIndexToMsgLog();

bool on_actionSaveLogFile_triggered();

void on_actionViewSentMsgHistory_triggered();

void on_sendMultiMessage_triggered();
void on_actionFilterView_triggered();

void monitorThreadFinished();
};


Mainwindow constructors:



MainWindow::MainWindow(QMainWindow *parent)
: QMainWindow(parent), clientConnected(false), appSettings(new AppSettings(this)) , msgTableModel(new QVcaCanMsgModel(this)), networkIfc(new QVcaNet(this)), monitorThread(NULL), sentMsgHistory(new QVcaCanMsgModel(this))
{
setupUi(this);
msgLog->setModel(msgTableModel);
//msgLog->verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
msgLog->setSortingEnabled(true);
msgLog->setColumnWidth(2, 130);
msgLog->setColumnWidth(4, 180);
msgLog->setDragEnabled(true);
connect(msgLog->verticalScrollBar(), SIGNAL(sliderMoved(int)),
this, SLOT(unsetFollowHeadCheckBox(int)));

connect(this, SIGNAL(msgHistoryUpdate(const QVcaCanMsg&)),
sentMsgHistory, SLOT(messageEnqueued(const QVcaCanMsg&)));
setAttribute(Qt::WA_DeleteOnClose);
}

MainWindow::MainWindow(QString settingsFileNamePath, QMainWindow *parent)
: QMainWindow(parent), clientConnected(false), appSettings(new AppSettings(settingsFileNamePath, this)) , msgTableModel(new QVcaCanMsgModel(this)), networkIfc(new QVcaNet(this)), monitorThread(NULL), sentMsgHistory(new QVcaCanMsgModel(this))
{
setupUi(this);
msgLog->setModel(msgTableModel);
//msgLog->verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
msgLog->setSortingEnabled(true);
msgLog->setColumnWidth(2, 130);
msgLog->setColumnWidth(4, 180);
msgLog->setDragEnabled(true);
connect(msgLog->verticalScrollBar(), SIGNAL(sliderMoved(int)),
this, SLOT(unsetFollowHeadCheckBox(int)));

connect(this, SIGNAL(msgHistoryUpdate(const QVcaCanMsg&)),
sentMsgHistory, SLOT(messageEnqueued(const QVcaCanMsg&)));
setAttribute(Qt::WA_DeleteOnClose);
}

gyre
27th November 2007, 15:08
But I dont get it...if none of those two widgets are not displayed when Im closing thw window everything works just fine...
Understand...those two widgets are actually QMainWindows too...MainWindow is just their parent....they dont part of mainwindow...they are displayed upon som Qaction

wysota
27th November 2007, 15:17
Show me the contents of the close event.

gyre
27th November 2007, 15:24
Close event:


void MainWindow::closeEvent(QCloseEvent *event)
{
if(maybeSaved()){
event->accept();
} else {
event->ignore();
}
}


Method maybeSaved():



bool MainWindow::maybeSaved()
{
QString fileName;
if(msgTableModel->rowCount()) {
QMessageBox::StandardButton ret;
ret = QMessageBox::warning(this, tr("QCanalyzer"),
tr("The Log is not empty.\n"
"Do you want to save actual Log?"),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
if (ret == QMessageBox::Save){
if(clientIsConnected()){
on_actionDetachInterface_triggered();
}
fileName = QFileDialog::getSaveFileName(this, tr("Save MsgLog File"), "/home/gyre/temp", tr("Text LogFiles(*.tlog);;Bin LogFiles(*.blog);;Any File(*)"));
if (fileName.isEmpty()){
return false;
} else {
return saveFile(fileName);
}
}

if(ret == QMessageBox::Discard){
if(clientIsConnected()){
on_actionDetachInterface_triggered();
}
return true;
}

if(ret == QMessageBox::Cancel){
return false;
}
} else if(clientIsConnected()){
on_actionDetachInterface_triggered();
return true;
}

return true;
}


Thanks man for dealing with this...

wysota
27th November 2007, 15:32
Ok, time for some tests. Comment out the close event and add destructors to both child window classes. Inside them just qDebug() something so that you can see if they are called. Do the same for your main window, run the application and see if all the messages are displayed when you close the app.

Oh, and don't start any additional threads. Comment out whatever is needed.

gyre
27th November 2007, 15:39
I put qDebug() in their destructors...they are called...the debug prints are printed correctly...but the process does not get finished and is still running
Ah...I havent commented out the close event...shit
Wait :)

gyre
27th November 2007, 15:44
Yes the behaviour stays the same...
They get closed, htey call their destructors but the proces does not get finished...
Even after I commented out the close event :(

wysota
27th November 2007, 15:52
Something is occupying the event loop then. Add the following code to your main():


class Mark : public QObject {
public:
Mark() : QObject(){ startTimer(1000); }
~Mark() { qDebug() << "Mark destroyed"; }
protected:
void timerEvent(QTimerEvent*){
qDebug() << QDateTime::currentDateTime();
}
};

int main(...){
//...
mainWindow->show();
Mark m;
return app.exec();
}

You should be receiving marks every second during all time the application runs. See if you still receive them after closing those windows.

gyre
27th November 2007, 15:58
Yes I still receive them after closing the MainWindow that closes child widgets...

gyre
27th November 2007, 16:13
Isnt the problem of this....setting that attribute of main window:
setAttribute(Qt::WA_DeleteOnClose); ??
That it gets delted before tha app event loop gets finished ?
When I deleted that attribute from codes...when I open on of those two child windows...then I close mainwindow it gets closed but those two widgets stay displayed and they dont get closed with the mainwindow...but when I close both of them then event loop finishes correctly...

wysota
27th November 2007, 16:27
Isnt the problem of this....setting that attribute of main window:
setAttribute(Qt::WA_DeleteOnClose); ??
That it gets delted before tha app event loop gets finished ?
When I deleted that attribute from codes...when I open on of those two child windows...then I close mainwindow it gets closed but those two widgets stay displayed and they dont get closed with the mainwindow...but when I close both of them then event loop finishes correctly...

The attribute is not necessary here and you can safely remove it. I'm having a hard time understanding - if you close the "main" mainwindow, do the other two get closed as well or not?

BTW. If you still receive marks, then either the lastWindowClosed() signal doesn't fire or some window remains open.

gyre
27th November 2007, 16:36
The attribute is not necessary here and you can safely remove it. I'm having a hard time understanding - if you close the "main" mainwindow, do the other two get closed as well or not?

Im going to explain it again...
I have Application MainWindow created. I have prepared two widgets that are in application created and displayed upon clicking on some button by calling their constructor with mainwindow as their parent..So the app mainwindow is their parent.
Now.. I execute app...mainwindow gets created. I click on a button that creates those two child widgets. Then I close the mainwindow (see Im closing the mainwindow before closing those two child widgets that were displayed upon clicking on some mainwindow button). SO I close the mainwindow and now 2 situations happen:

1) If I dont have set this attribute: in mainwindow setAttribute(Qt::WA_DeleteOnClose);
then those two child widgets dont get closed with the main window and they stay displayed even after its parent widget was closed

2) If I set this attribute in mainwindow then those two widgets get destroyed with the mainwindow but the application eventloop is still running apparenetly because the proces in which application is running doesnt get finished....

I hope I was clear now

wysota
27th November 2007, 16:42
Ok, I made a check - if a widget has the DeleteOnClose attribute set, it won't make the application emit lastWindowClosed() signal and thus the application won't quit.

Here is the sample code:

#include <QMainWindow>
#include <QApplication>
#include <QPushButton>
#include <QLayout>

class MW : public QMainWindow {
Q_OBJECT
public:
MW() : QMainWindow(){
QWidget *w = new QWidget;
QVBoxLayout *l = new QVBoxLayout(w);
b1 = new QPushButton("W1");
l->addWidget(b1);
b2 = new QPushButton("W2");
l->addWidget(b2);
connect(b1, SIGNAL(clicked()), SLOT(doIt()));
connect(b2, SIGNAL(clicked()), SLOT(doIt()));
//setAttribute(Qt::WA_DeleteOnClose);
setCentralWidget(w);
}
private:
QPushButton *b1, *b2;
private slots:
void doIt(){
QMainWindow *mw = new QMainWindow(this, Qt::Window);
mw->setAttribute(Qt::WA_DeleteOnClose);
mw->show();
}
};

#include "main.moc"

int main(int argc, char **argv){
QApplication app(argc, argv);
MW mw;
mw.show();
// MW *mw = new MW;
// mw->show();
return app.exec();
}

If you run the code, it works as expected in that way that it quits the app when the last of the windows gets closed, but it doesn't close those child windows. If you uncomment the commented entries and comment out their counterparts, closing the main window will close and delete the children, but will not quit the application.

A solution is not to use DeleteOnClose attribute on the main window and to delete the child windows in the close event if you need such behaviour. Then it will all work as expected. An alternative is to call QCoreApplication::quit() in the close event of the main window or to connect the destroyed() signal of the main window to the quit() slot of the application. Then you'll be able to use DeleteOnClose.

gyre
27th November 2007, 16:48
A solution is not to use DeleteOnClose attribute on the main window and to delete the child windows in the close event if you need such behaviour.

Yes but HOW ? How do I delete child windows in close event ? is there a method that enable me to do that ?

wysota
27th November 2007, 16:57
Yes but HOW ? How do I delete child windows in close event ? is there a method that enable me to do that ?

Hmmm... I might not be understanding the question, because the answer seems obvious - call delete or deleteLater() on each of the child windows.

But I'd suggest connecting the destroyed() signal of the main window to the app's quit() slot.

gyre
27th November 2007, 17:52
Hmmm... I might not be understanding the question, because the answer seems obvious - call delete or deleteLater() on each of the child windows.


But the child widget pointer is created in specific method of mainwindow...so how do I call delete on that pointer ?



But I'd suggest connecting the destroyed() signal of the main window to the app's quit() slot.


Ill try this

gyre
27th November 2007, 18:02
it works! :)
Thanks ! :)

wysota
27th November 2007, 19:35
But the child widget pointer is created in specific method of mainwindow...so how do I call delete on that pointer ?
For example you can store the pointer in the window object.