PDA

View Full Version : How to reimplement closeEvent, in the main window of an MDI application



barebones
8th August 2009, 14:21
uname -a: Linux bartleby 2.6.28-14-generic #47-Ubuntu SMP Sat Jul 25 01:19:55 UTC 2009 x86_64 GNU/Linux
moc -v: Qt Meta Object Compiler version 61 (Qt 4.5.0)

Hello, gents,

In an MDI application, what is the correct way to reimplement closeEvent in the main window (the one that contains a QMdiArea)?

Assume that the MDI child class (derived from QMdiSubWindow) reimplements closeEvent, popping up a message box with discard/save/cancel buttons, and accepts or ignores the event appropriately.

Trying to close the main window should attempt to close the children (f.i., QMdiArea::closeAllSubWindows), and NOT exit the application if one of them ignored its close event.

The funny thing is, after some hours on my own, I tried compiling TrollTech's own example, at
http://doc.qtsoftware.com/4.5/mainwindows-mdi.html

and it fails just the same. Try to open an MDI child, write some text on it, and close the main window: it will ask for the document to be saved first. If you choose "Cancel", the applications closes anyway!

Has anybody successfully worked around this? Could you try if your own MDI applications stay open when canceling a save on a child?

Thanks for your comments in advance.

- - -

Thinks I have tried, in a minimal MDI application of my own:

Test conditions:
* The MDI child window implements closeEvent, popping a message box with discard/save/cancel buttons, and ignores or accepts the event accordingly.
* The main window starts with only one MDI child. In this minimal test, the child is always considered "dirty" (if you close it, it will pop up a message box requesting to be saved).
* Closing the MDI child works as expected, for the three cases of the message box. The question is, what happens when closing the main window.
* There are good old "printf"s all around, to trace the flow of events.

Alternatives tested:

1) Not reimplementing closeEvent in the main window at all.
Effect: the MDI child won't get a close event, and die without any message box.

2) The implementation of closeEvent in the main window calls mdiarea->closeAllSubWindows(), and nothing else.
Effect: the MDI child pops up the message box; when pressing "Cancel", the closeEvent in the MDI child correctly ignores the event. But the main window closes anyway.
Comment: as expected, since I have not ignored the event in the main window itself. The question is, on which condition?

3) The implementation of closeEvent in the main window calls mdiarea->closeAllSubWindows(), and afterwards accepts or ignores the event depending on whether mdiarea->activeSubWindow() returns zero or non-zero.
Effect: same as in the TrollTech example - the MDI child pops up the message box; when pressing "Cancel", the closeEvent in the MDI child correctly ignores the event. But mdiarea->activeSubWindow() returns zero nevertheless (why??), and the main window closes.
Comment: no surprise here, since this is what the TrollTech example is doing.

4) The implementation of closeEvent in the main window calls mdiarea->closeAllSubWindows(), and afterwards accepts or ignores the event depending on whether mdiarea->subWindowList().isEmpty() is true or not.
Effect: the opposite, sort of. Closing the main window gets the MDI child pop-up, and pressing "Cancel" keeps both child and main window alive. So far so good. But when closing the main window and pressing "Close without saving" (discard) instead, the MDI child accepts the event and closes (good), but mdiarea->subWindowList().isEmpty() still returns false and the main window remains open (bad).

So, what is the condition for accepting or ignoring the closeEvent in the main window?

Apparently, any solution would depend on the order in which events are fired (which is not good).

barebones
9th August 2009, 16:44
If somebody is interested, a workaround is:

In the main window's closeEvent implementation, after calling QMdiArea::closeAllSubWindows() but before testing for QMdiArea::activeSubWindow(), call QMdiArea::activateNextSubWindow(). This seems to do the trick, although I have not tested extensively.

Still, the example application needs to be fixed.

barebones
9th August 2009, 23:08
I just found that the close() slot of widgets returns a bool, with a value precisely intended for the case at hand. So a better solution is probably not to use closeAllSubWindows() at all, and code instead the closeEvent as follows:


void MainWindow::closeEvent (QCloseEvent* event)
{
QList<QMdiSubWindow*> list = mdi->subWindowList ();

for (int i = 0; i < list.size (); i++)
if (!list[i]->close ()) {
event->ignore ();
return;
}

event->accept ();
}

which will stop the procedure at the first MDI child that cancels the event.

I'm open to hear how other people have coded their MDI apps' closeEvent.