PDA

View Full Version : What happens after closing and before destruction?



Raccoon29
17th April 2008, 11:20
Hi all,

I have two related questions to ask:
1) Is it admitted to set the flag Qt::WA_DeleteOnClose for a QWidget, beyond that for the MDI subwindow that contains it?

If the answer to the 1 is "Yes", the second question:
2) What does a QWidget do right after accepting the closeEvent and before its desctruction?
Because right now this function:


void frmarticle::closeEvent(QCloseEvent *event)
{
if(!maybeSave()) // check for modifies apported
{
event->ignore(); //modifies apported stop the closement
return;
}

viewport->close(Article); //nullifies pointers
event->accept(); //accepts close event, it is supposed to destroy this widget now, is it?
}
// now quits the closeEvent and the program raise exception, before calling the destructor

So, if I would know what it does after close event and before destructor, I could debug it; if you have any other suggestion don't be shy, I accept everything.

Raccoon29
17th April 2008, 18:04
:eek: No one know where control goes after closing a Widget in a MDI system??

wysota
17th April 2008, 22:43
You can use WA_DeleteOnClose for any widget you want. After an event is accepted, the control is returned to the event loop and the widget is most probably scheduled for deletion using deleteLater() (I'm not sure of that, but that's a quite safe thing to assume) and is deleted when the event queue reaches the delayed destruction event.

I'd suggest using a debugger to see what and why crashes. Most probably you either have a null or invalid pointer or you are trying to delete an object inside a slot connected to a signal originating from the object itself.

Raccoon29
18th April 2008, 12:54
Thank you, you have just made me find the courage to install the debug environment :)

Once I have debugged it, I'll post here more informations.
Thank you for the suggestion.

Raccoon29
18th April 2008, 16:06
Ok, debug installed and working, from its scan resulted the following (now we'll see how much deep is your Qt knowledge :) ):

The application after closeEvent and before destruction stopped the run with the message:
ASSERT failure in QList<T>::at: "index out of range", file ../../include/QtCore/../../src/corelib/tools/qlist.h, line 391

What is the verdict?

wysota
18th April 2008, 16:41
"after closeEvent" meaning "after closeEvent returns" or "after closeEvent is called"? It'd say your viewport->close(Article) tries to reference some item in some list and fails because the list is smaller than it expects.

Raccoon29
18th April 2008, 17:13
"after closeEvent" meaning "after closeEvent returns" or "after closeEvent is called"?

meaning "after closeEvent accepts the event and return", and I remember that the widget closed is a MdiSubWindow with WA_DeleteOnClose that contains frmarticle (inherited from QWidget)


It'd say your viewport->close(Article) tries to reference some item in some list and fails because the list is smaller than it expects.

Yes, I thought it too, but viewport->close(Article); is called to do just this:



void CViewports::close(EViewport win)
{
switch(win)
{
// Article
case 1:
{
if(pArticle) //pointer to frmarticle, not null for sure
{
pArticle=NULL;
wArticle=NULL;
}

break;
}
[...]
}

Maybe, since the problem seems to be some list, could be the QTableWidget present in frmarticle be involved in the problem, or I'm totally off-road?

aamer4yu
18th April 2008, 17:18
The application after closeEvent and before destruction stopped the run with the message:
ASSERT failure in QList<T>::at: "index out of range", file ../../include/QtCore/../../src/corelib/tools/qlist.h, line 391

Try to trace the lists you are using in the application.

Raccoon29
18th April 2008, 17:44
Try to trace the lists you are using in the application.
I would do it, but I'm not using QLists at the moment, and noteven objects that inherit from it. :(

wysota
18th April 2008, 19:14
Of course you are. The mdi window keeps a list of its child windows. You are interacting with it when closing a child window. Maybe you simply shouldn't use the attribute. I suggest you take a look at the source code of the mdi framework and see what happens if you close a child window.

Raccoon29
18th April 2008, 19:26
Of course you are. The mdi window keeps a list of its child windows. You are interacting with it when closing a child window. Maybe you simply shouldn't use the attribute. I suggest you take a look at the source code of the mdi framework and see what happens if you close a child window.
Yes,yes, you are right, I noticed it but only after the last post, and then I forgot to edit.
Anyway I've given a look a the source like you suggest, but still the problem is in the shadow.
I found the assert that fails, but still I don't understand why; I'm going to break my head for the second time trying to understand how that subwindows list index works :)

EDIT: I've already tried to cutout the attribute, but the problem persists.

wysota
22nd April 2008, 21:04
Let's try from the beginning... Could you describe the goal you are trying to obtain? Maybe instead of trying to fix your solution we'll find some other solution that works.

Raccoon29
23rd April 2008, 11:11
Could you describe the goal you are trying to obtain?
My goal is to close the windows without the program be destroyed :)

The trouble is that the description requires many "if", anyway here follows a "once for all" description.

=ENVIRONMENT=
It is a QMdiArea workspace.
There are three QMdiSubWindows: ViewArticles that makes open Article that makes open Manager. I say "makes open" because between them there are no dependencies, no one of them is parent of the other: they all refer to the master class CViewports which manage them all just opening and closing them.
Every QMdiSubWindow has the WA_DeleteOnClose attribute.

=PROBLEM=
Now, when Article calls Manager (to do a quick search), the same Article becomes a wandering bomb, because if it get closed, the whole program explodes with the famous error:

ASSERT failure in QList<T>::at: "index out of range", file ../../include/QtCore/../../src/corelib/tools/qlist.h, line 391

=DEBUG CLUES=
-Opened Article, opened Manager, closed Manager (now Article is expected to boom on close, but...), made lost focus to the Article window (so give focus to ViewArticles, or just switch to another program and then come back) and Article get defused and even closed the program continues normally.

-Other windows (out of Article) call Manager, and all of them become wanding bombs just like Article.

-If Manager get not called all works as expected.

-When closing Article before assertion failed, the number of opened windows in QMdiArea::subWindowList seems to be right (=2).


These are the main clues I found, I would post some code too, but there are over twenty files, so I'll post them on request if needed.
But anyway what is important and that I really can't understand is why the loss of the focus heals the error! :confused:

Every question is welcome, just I'd like to know why all this! :crying:

wysota
23rd April 2008, 11:53
One step at a time...

Let's try with a minimal application that consists of an mdiarea that has a child window that can open other windows in the mdi space using a button or something like that. No special attributes or anything. When you have that done and verify it does not crash, you can continue with putting some logic of yours there.

Raccoon29
23rd April 2008, 17:08
I'm trying to reproduce the error in a minimal application as you suggested, but still the problem has not shown itself.
I attached the source of the minimal application, maybe in the meanwhile some one of you could make the error raise, playing around...

wysota
23rd April 2008, 17:34
If it works fine then try to isolate some other part of your code and implement or paste it into the test environment you just built.

Raccoon29
23rd April 2008, 17:52
If it works fine then try to isolate some other part of your code and implement or paste it into the test environment you just built.
Ok, I'm trying it.

Meanwhile, do you have any sort of idea of what is this dued to? Or do you have any idea on why the focus should heal it, what sort of signals could emit the focus?

wysota
23rd April 2008, 20:23
If I had, I would have already told you.

mchara
24th April 2008, 07:55
Hi,
I'm using Qt::WA_DeleteOnClose with MDI sub windows and it works fine, so i think it's some bug in your code(not qt's).
Qt docs says that mdi sub window should have set Qt::WA_DeleteOnClose because QMdiArea won't work properly.

When you create your own subwindow, you must set the Qt::WA_DeleteOnClose widget attribute if you want the window to be deleted when closed in the MDI area. If not, the window will be hidden and the MDI area will not activate the next subwindow.

However i think it may be a problem with Qt::WA_DeleteOnClose defined twice - on QMdiSubWindow itself and on widget contained by this QMdiSubWindow.
This could produce a crash because hiding subwindow deletes subwindow & contained widget & then widget is removed once again.

I'm not sure about this, but if QMdiSubWindow have Qt::WA_DeleteOnClose attribute set(and it probably have set it by default), you shouldn't set it on widget of sub window.

wysota
24th April 2008, 09:53
I'm not sure about this, but if QMdiSubWindow have Qt::WA_DeleteOnClose attribute set(and it probably have set it by default), you shouldn't set it on widget of sub window.

If the widget is not deleted explicitely in the destructor by calling delete on it, it shouldn't be a problem - QObject will "disconnect" the object from the list of its children so it shouldn't be deleted again by the parent as it has no parent anymore.

mchara
24th April 2008, 10:40
If parent is removed after child - you're right,
but if parent is removed first(and removes child) and then child is removed explicitly, then we have wrong pointer there.
And what if both generate deleteLater events on hide?
Is it managed on the level of event loop?
If hide event is called on parent, it calls hide on child, and both generates deleteLater events,
so if then delete event of parent would be processed (and delete also child), child's delete event is already queued...


One more thing worth a check is creating widget normally embedded in mdi sub window without it (just create & show standalone) and delete it (via delete, deleteLater or close with DeleteOnClose attrib) and see if problem isn't inside of that widget.

By the way wysota,
Great article about undo/redo framework in qt quarterly ;)

wysota
24th April 2008, 10:52
but if parent is removed first(and removes child) and then child is removed explicitly,
It is not removed explicitly. By explicit I mean that you delete it manually instead of letting Qt handle that.


then we have wrong pointer there.
No.

#include <QApplication>
#include <QWidget>


int main(int argc, char **argv){
QApplication app(argc, argv);
QWidget *parent = new QWidget(0);
parent->setAttribute(Qt::WA_DeleteOnClose);
QWidget *chld = new QWidget(parent);
chld->setAttribute(Qt::WA_DeleteOnClose);
parent->show();
return app.exec();
}

The above code works just fine.



And what if both generate deleteLater events on hide?
That's exactly what happens with the attribute set.

Is it managed on the level of event loop?
Yes, the event loop handles that.


If hide event is called on parent, it calls hide on child, and both generates deleteLater events,
so if then delete event of parent would be processed (and delete also child), child's delete event is already queued...
But it doesn't get executed. When an object is deleted, pending events are discarded (that's not exactly what happens, but the result is valid).


By the way wysota,
Great article about undo/redo framework in qt quarterly ;)
Thanks. It looked much worse before they corrected the language ;)

Raccoon29
24th April 2008, 11:18
Thank you for your ideas so far, please keep it up :)

New clue: if I always ignore the closeEvent of Manager (the third bastard window), the window keeps opened, but the problem disappears. Anyway this is just a dodge, I have to find the reason.


One more thing worth a check is creating widget normally embedded in mdi sub window without it (just create & show standalone) and delete it (via delete, deleteLater or close with DeleteOnClose attrib) and see if problem isn't inside of that widget.
Great idea this one! I didn't try it.
I'll try it, then I'll report how it goes.

Raccoon29
25th April 2008, 11:51
I tried two tests:
1) make the Manager a simple dialog not MDI
2) set in the MdiSubWindow a Manager created at the moment of adding
results:
1_ Manager isn't of course in the MdiArea anymore, but no error there, everything works fine
2_ For compatibility reasons it wasn't a precise try, but anyway I really think that this won't be usefull

I'm going to try a neutre widget in the MdiSubWindow (not Manager, but a simple QWidget) to see if is a Manager internal code bug, might this be usefull?

Useless to say that every idea or possible clue are still much apprecciated

EDIT: @mchara if your idea is not between the tryies I did, please tell me, it would be an added test

mchara
25th April 2008, 12:36
Both tests should tell us where to search for a bug i think,
or at least exclude some potential reasons of a crash.

So...
let us know about results of second test and i hope it will give us some new clues.

Raccoon29
30th April 2008, 12:27
I tried the neutre QWidget, but the problem persists.
Is it possible that setFocus() for widgets has a point of "abuse"?
I ask this because during the tests I found another Qt assertion failed that fixed when I removed a couple of setFocus(). So, since the problem disappears when making the program window lose the focus (that focus trick), I was wonderign if this two behaviors can be correlated?
Can Mdi have some weak point when using (or even abusing) setFocus method?

wysota
4th May 2008, 22:43
I'd really suggest looking for the problem in your code and not Qt.

mchara
5th May 2008, 07:49
Can Mdi have some weak point when using (or even abusing) setFocus method?
I think not,
Your MDI manager might have some...
Tests you performed suggests that error is in your code - if child widget works if it's not in mdi area and empty widget placed in mdi area causes a crash the problem is surely somewhere in MDIArea subclass implementation or on it's communication with the rest of application.

If you have problems with setFocus() used in your code you should check if pointers are valid everywhere.

If it still doesn't gives you a clue, try to trace call stack (when debugging after a crash) - it should show a method where you're doing something wrong.
Or if call stack doesn't says anything useful, it's usually some slot executed by a signal emition.

Raccoon29
5th May 2008, 20:17
I'd really suggest looking for the problem in your code and not Qt.
I know the problem is in my code, it must be there.
But to know what part of my logic is wrong, I have first to understand why the problem raises, and since the problem is a Qt library assert fail, I have to understand for what reasons that assert fails. It fails because MDIsubwindow indexes are out of range. Great. But I never touch Mdisubwindow indexes (that's Qt stuff), so the problem begins even further!
I'm sure that Qt is stable enhough to resist to newbies attacks too. :p

Raccoon29
6th May 2008, 12:50
If it still doesn't gives you a clue, try to trace call stack (when debugging after a crash) - it should show a method where you're doing something wrong.
Or if call stack doesn't says anything useful, it's usually some slot executed by a signal emition.

The trace just says me that the close method begins everything, but that was known.
Maybe I have found a clue, but I need to ask you a question:
when the control is in the Widget contained by the MdiSubWindow, what would be the best way to close the subwindow programmaticaly?

I mean, by now when I have to close the window after an operation is complete, I do:


void frmmanager::close()
{
viewport->close(Manager); // just nullifies Manager pointers
viewport->getMDI(this)->close(); // calls the close of the MdiSubWindow
}

where viewport is a my class that manages subwindows' pointers, and getMDI returns the pointer of the Mdi that contains the widget I pass it (like QMdiSubWindow::widget(), but reversed).
The target is to close the MdiSubWindow, because if I call the Widget's close, the QWidget closes, but its MdiSubWindow keeps opened (empty but opened).

Is this a self-destructive way to close it? Maybe there is a better way to achieve that?

mchara
12th May 2008, 12:08
I think you shouldn't close subewindows at all when main window closes. Qt surely propagates close events to widgets children in a background.


And one more thing that can be a reason - when you are overriding events always remember to call ancestor method i.e.:


void frmmanager::close()
{
QMainWindow::close();// OR WHATEVER frmmanager INHERITS

viewport->close(Manager); // just nullifies Manager pointers
viewport->getMDI(this)->close(); // calls the close of the MdiSubWindow
}


Qt may do something important in those events, so if you're not calling ancestors events you may cause qt's internal implementations works wrong.

It's not always a problem cause there's lots of events that does nothing and are meant only to us to override them, but not always it is so.

BTW: sorry for late response.

Raccoon29
12th May 2008, 18:03
I think you shouldn't close subewindows at all when main window closes. Qt surely propagates close events to widgets children in a background.
Yes, I agree with you. But I'm trying to close programmatically a MdiSubWindow along the run, not at the end.
I sent an image that should explicate the situation.


And one more thing that can be a reason - when you are overriding events always remember to call ancestor method
Oh, I didn't know that, thank you... but the problem persists.


BTW: sorry for late response.
Don't mind it: better a late answer that no one :)

mchara
13th May 2008, 07:42
Yes, I agree with you. But I'm trying to close programmatically a MdiSubWindow along the run, not at the end.


Still there should be only one close() called on child window of mdi area.

You may also make a breakpoint at frmmanager::close() and pass it step by step, to see which line causes crash.

Check also if you're not using any pointers to sub windows without checking if it is still valid(with deleteOnClose flag window is removed after closing).
You may tray calling setWindowFlags on subwindows (when created) to ensure it's not removed on close.
And see also QPointer<> class - it sets itself to NULL if pointed object was removed elsewhere.

Raccoon29
14th May 2008, 13:01
You may also make a breakpoint at frmmanager::close() and pass it step by step, to see which line causes crash.
Just after the closeEvent end (when exiting the function), when the control passes to Qt that has to hide the window, then there get reached the ASSERT that fails in qmdisubwindow.


You may tray calling setWindowFlags on subwindows (when created) to ensure it's not removed on close.
Sorry, I miss your aim: maybe you mean to make the window don't destroy on close removing WA_DeleteOnClose?

Anyway the program doesn't crash when I use the X button, but it does when I call my close() method, which just does a call to

viewport->getMDI(this)->close();
but this is ehough to make it explode.

Could it be that calling this function I have the same Widget, where I call close from, that gets destroyed, and then when the controls come back to it, the run crashes because that widget doesn't exist anymore?

mchara
14th May 2008, 15:25
J

Sorry, I miss your aim: maybe you mean to make the window don't destroy on close removing WA_DeleteOnClose?


Yes, this is what i meant, sorry if it wasn't clear.


However, i thought about one more think - i had a bug other times i was removing some item in a slot and i missed that the slot was in instance that was removed. I mean maybe close called in you slot tries to delete instance while you're still executing slot in same instance.

So my suggestion is try not using close event at all.
i realized that close() is a slot , so you can simply connect buttons clicked() signal with sub window's close() slot. then you're sure that things happens in different eventloop iterations and code execution won't stay in implementation of removed object.

wysota
14th May 2008, 20:08
If you don't destroy a window when it is closed, the next window will not get activated. That's probably not what you want.

mchara
15th May 2008, 07:31
I didn't knew that, but it was suggested rather to check if crash is related to deleting or not.

wysota
15th May 2008, 09:40
You exchanged 15 posts without getting nowhere. I didn't want to cut in as obviously you chose another path to detect the problem, but since you are not making progress maybe it is time to try some other approach? I suggest taking a debugger, setting some breakpoints and stepping through the code. It will probably take two hours or so to detect the problem but then you'd be able to close in on the bug and fix it within a day.

Raccoon29
15th May 2008, 13:09
So my suggestion is try not using close event at all.
I tried bypassing the closeEvent, but nothing.


i realized that close() is a slot , so you can simply connect buttons clicked() signal with sub window's close() slot
Great idea this one, I've tried to connect the button to the QMdiSubWindow close, but it seems like if the slot is not activated, maybe this row is not correct:

QObject *mdi=(QObject*)viewport->getMDI(this);
connect(ui.tlbclose,SIGNAL(clicked()),mdi,SLOT(clo se()));



I suggest taking a debugger, setting some breakpoints and stepping through the code. It will probably take two hours or so to detect the problem but then you'd be able to close in on the bug and fix it within a day.

All respect, but I'm using a debugger since third/fourth post of this thread.
If I have to put a signature somewhere to get it solved within a day, well, just tell me where ;)


[...]but since you are not making progress[...]
I disagree.
As I am a developer and debugger as you are as well (I guess), you know that every little clue is important to approach the solution; so here I was collecting every possible clue from people that have much more Qt experience, knowing many more tricks and behaviors than me.

wysota
15th May 2008, 13:13
All respect, but I'm using a debugger since third/fourth post of this thread.
And what did you find out? Where did you place the breakpoints and which function of yours causes the program to enter an invalid state?


As I am a developer and debugger as you are as well (I guess), you know that every little clue is important to approach the solution; so here I was collecting every possible clue from people that have much more Qt experience, knowing many more tricks and behaviors than me.

We're just guessing here. If we can't reproduce a bug, how can we solve it? You are the one having access to the buggy code so just take that debugger and find out which list causes the assert.

mchara
15th May 2008, 15:42
try

connect(ui.tlbclose,SIGNAL(clicked(bool)),mdi,SLOT (close(void)));

connect returns bool that is true if connection was successfull.

And improper connects generates also messages (output window of visual studio)

Raccoon29
16th May 2008, 12:26
We're just guessing here. If we can't reproduce a bug, how can we solve it? You are the one having access to the buggy code [...]
wysota, you are right. :o

Ok, guessing is over.
Today I "lost" my morning, but now I have the essential code to reproduce the bug, I attach it here.

Environment
Qt: v 4.3.4
OS: Windows Xp Professional
Compiler: MinGW make 3.80 (mingw32-make)
Qmake: v 2.01a

How to reproduce the bug
From the program start:
1) Menu: Anagraph->Article managing
2) Click on "New" button
3) Position the cursor on a short field below and press F4
4) Close the appeared window by "Close" button (not the X)
5) Close the "Article data" window by "Close" button (not the X)
now the program should exit unexpectedly.

If between points 4 and 5 you give focus to an underneat window (or to another program), the program will not exit and will close the "Article data" window normally (the infamous focus trick).

So, what to say... have a nice debugging :D

I want to thank you all very much for being still giving help to me, even in a so desperate situation... I'm touched :crying: , no other forum gives so much!

wysota
17th May 2008, 19:56
Hmm... It seems I am unable to reproduce the bug with Qt 4.4. Which version of Qt are you using? Qt 4.3.4 indeed segfaults. The reason is probably that you destroy a window that contains a button that is currently the source of an emitted signal. Calling deleteLater() instead of deleting the object immediately might be enough to overcome the problem. I'm not sure what your "viewport" does, though, so the problem might be completely elsewhere. Based on what the debugger says, the crash happens while the click on the button is being handled. Taking a look at QMdiSubWindow::closeEvent() and QMdiArea::viewportEvent() might reveal the exact cause.

Raccoon29
19th May 2008, 16:03
It seems I am unable to reproduce the bug with Qt 4.4. Which version of Qt are you using? Qt 4.3.4 indeed segfaults.
Uhm, I see.
So, does it mean that upgrading to Qt4.4 fixes the problem? (As I wrote, mine is Qt 4.3.4)


Calling deleteLater() instead of deleting the object immediately might be enough to overcome the problem.
But I don't delete it, I leave this task to the WA_DeleteOnClose...


I'm not sure what your "viewport" does, though, so the problem might be completely elsewhere.
"viewport" is nothing special, it just wraps the opening/closing of the mdisubwindows, and several other not dangerous functions (hidden in the debug version I sent you).

Raccoon29
19th May 2008, 17:41
I can't believe it! Finally we solved it!
:crying: touched...

What I did to solve it:

1_ I forced off the automated desctruction disabling WA_DeleteOnClose


void CViewports::open(EViewport win, QWidget* parent)
{
[...]
wArticle->setAttribute(Qt::WA_DeleteOnClose,false); //forced off for every widget
[...]
}


2_ CViewports desctructor uses deleteLater() instead of delete for every widget,ensures that every widget is destroyed in the right way:


if(pArticle)
{
//delete pArticle;
pArticle->deleteLater();
pArticle=NULL;
wArticle=NULL;
}


3_ Every closeEvent (of every widget) calls its destruction with deleteLater(), instead to delegate it to WA_DeleteOnClose:


void frmcustomerorder::closeEvent(QCloseEvent *event)
{
if(!maybeSave())
{
event->ignore();
return;
}

viewport->close(CustomerOrder);
//event->accept(); -> no more relies on WA_DeleteOnClose
deleteLater(); // calls its destruction
}


And in this way it is working (at least until now).

After N posts we finally found the solution, this forum is simply great.
Thank you from deep my heart to everyone! :crying:

By the way: wysota, you gave me the main tip to solve it (with deleteLater), and since you answered kindly to my (I admit) boring request, from now on, I'll call you SaintWysota (or shorted S.wysota) :D

wysota
20th May 2008, 10:33
Oh, I am not saint :)

As for your problem - it must have been related to the signal emission I mentioned.