[Solved] QFileDialog::getOpenFileName breaks message boxes etc.
-
[quote]For what do you need an event loop in your example?
You want to open fife modal dialogs, one after each other, no event loop is needed for that.
You need an event loop for mode less dialogs, mode less windows or if you have a system of objects, that react on signal/slot in a not synchronous way (no queued connection, no threads)[/quote]
I agree with Gerolf, however, I'm still interested in why it's happening and will report here if I find anything. In the meantime, just use a workaround.EDIT: I found out why the problem doesn't occur with a visible window. Consider the following code from QApplication:
@
void QApplicationPrivate::emitLastWindowClosed()
{
if (qApp && qApp->d_func()->in_exec) {
if (QApplicationPrivate::quitOnLastWindowClosed) {
// get ready to quit, this event might be removed if the
// event loop is re-entered, however
QApplication::postEvent(qApp, new QEvent(QEvent::Quit));
}
emit qApp->lastWindowClosed();
}
}@
EDIT2: This also doesn't happen if native dialog aren't used (The event loop gets reentered immidiately, but native widgets don't use the Qt event loop, so the application has enough time to process the quit event - it is actually done explicitly in the function that calls the native widgets - very interesting). -
Hi,
I tried with no message loop, and it worked as expected:
@
void mydoMain() {
// check that the message box is working as expected!
QMessageBox::information(0, "Test Msgbox", QObject::tr("Qt Version: %1").arg(QT_VERSION_STR));QFileDialog::getOpenFileName(0, "Open some file", QString(), QObject::tr("All files (*.*)"), nullptr, QFileDialog::ReadOnly); QInputDialog::getText(0, "Enter a text", "Blablabla:"); // you'll hear the message box sound, but don't see anything QMessageBox::warning(0, "Test Msgbox 2", "Hi, I'm another msg box"); // the error msg will show up, but it will stay "unusable" in the background // also the program will execute the following file dialog at the same time without waiting QErrorMessage ErrMsg; ErrMsg.setModal(true); ErrMsg.showMessage(QObject::tr("This is some sort of error message")); ErrMsg.exec(); // this pops up too early QFileDialog::getOpenFileName(0, "Open another file", QString(), QObject::tr("All files (*.*)"), nullptr, QFileDialog::ReadOnly); cout << "doMain() done. Time to quit()." << endl;
}//end method
int main(int argc, char *argv[]) {
QApplication app(argc, argv);mydoMain(); return 0;
}//end main
@ -
Yes you didn't start the event loop. Use QTimer. And consider replacing the FileDialog with QApplication::processEvents();.
I would say this qualifies as a bug. -
[quote author="Gerolf" date="1308939653"]For what do you need an event loop in your example?
[/quote]
The example is a minimal stripped down version of my real project, just to demonstrate this problem (and to avoid the dependencies of including Boost and OpenCV).In my project (a framework actually), I want to later have the option to compose everything into a big GUI. Certain parts of the system might get their parameters programmatically for batch processing, but if they don't, they show these dialogs to ask the user "on the fly". So I definitely want to have the option to use my classes in a Qt project where a global event loop is running!
Right now I have a modeless QGLWidget which reacts on key-presses, which probably needs an event-loop anyway. And I sometimes have multiple OpenCV Windows of their HighGUI subsystem with Qt elements, which also needs an event loop I think.
[quote author="Gerolf" date="1308939653"]
You want to open fife modal dialogs, one after each other, no event loop is needed for that.
You need an event loop for mode less dialogs, mode less windows or if you have a system of objects, that react on signal/slot in a not synchronous way (no queued connection, no threads)
[/quote]
Just to make absolute sure there is no misunderstanding: I want the full capabilities of a Qt GUI application, including a global event loop to have the freedom of later adding a main window or any other GUI element.The big question to me is: Did I break thinks by using modal input/file dialogs in a strange unusual way, or might this really be some Qt bug or quirk? It just doesn't make any sense to me why this example code doesn't work the way it should...
Thanks for the comments.
-
[quote author="loladiro" date="1308942428"]Yes you didn't start the event loop. Use QTimer. And consider replacing the FileDialog with QApplication::processEvents();.
I would say this qualifies as a bug.[/quote]
During my tests, I tried to put "qApp->processEvents();" and "qApp->sendPostedEvents();" between the dialogs -- no luck. -
I have an idea what could happen:
I was playing around a bit more now. Without event loop, everything is fine. With a visible main window, everything is fine. with event loop and without main window, it does not work.
why? QApplication has a signal lastWindowClosed, which is emitted, if the last top level widget is closed:
bq. By default,
- this attribute is set for all widgets except transient windows such as splash screens, tool windows, and popup menus
- QApplication implicitly quits when this signal is emitted.
so, to work around it, use a property: "QApplication::quitOnLastWindowClosed":http://doc.qt.nokia.com/4.7/qapplication.html#quitOnLastWindowClosed-prop and set it to false, and voila, it works :-)
@
int main(int argc, char *argv[]) {
QApplication app(argc, argv);CMainWidget w; // w.show(); app.setQuitOnLastWindowClosed(false); QTimer::singleShot(0, &w, SLOT(doMain())); return app.exec();
}//end main
@This means, it's not a bug, it's a feature :-)
-
Yes, I meant QApplication::processEvent() is the problem here. Sorry if I was unclear. I'll reupload your example in two files, so that other people can see the problem.
So: Problem with comment why (Please use this code for tests):
http://dl.dropbox.com/u/32965023/bugTest/bugTest.pro
http://dl.dropbox.com/u/32965023/bugTest/main.cpp
http://dl.dropbox.com/u/32965023/bugTest/test.hEDIT: @Gerolf, yes see my previous comment as to why it happens
EDIT2: Well yes, it might be a feature, but the problem is that QWidget removes that event again, as soon as another one gets created (If there's no processEvents in between. I think the same code should be added to native widgets -
Hi loladrio,
this solves the issue:
@
app.setQuitOnLastWindowClosed(false);
@No processEvents needed
-
Yes I know. My problem with the issue is more conceptual not practical. IMO, Qt should behave the same way independent of whether QFileDialog uses a native window or not. But in this point it doesn't.
-
[quote author="Gerolf" date="1308943261"]
This means, it's not a bug, it's a feature :-)[/quote]
Wow, thank you so much!!! Best one-line-fix ever ;-)However, there is one strange thought left: Why do several standard dialogs show different behaviours? How come that message and input boxes will silently "go away", a QErrorMessage will "hang", and QFileDialogs won't be impressed at all by this QApplication::quitOnLastWindowClosed property (try it out, file dialogs are still working in that state).
And then the differences in behaviour depending on which constructor you use and whether it's a native look or not -- that's just odd.
But anyway, thanks again, huge relief for me. Solved!
-
This is part of the QWidget constructor: IMO, it should also be executed when constructing native windows.
EDIT: Oops, forgot code
@
if (isWindow() || parentWidget()->isVisible()) {
// remove posted quit events when showing a new window
QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
@ -
But QFileDialog with use native dialog opens a nativ windiows file dialog. This does not look at the Qt event loop.
[quote author="loladiro" date="1308943850"]@
if (isWindow() || parentWidget()->isVisible()) {
// remove posted quit events when showing a new window
QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
@[/quote]This is the close event. The signal quit is different and as it is in the same thread, it's executed directly.
-
No it's not and that on purpose so that if there is a brief period with no active widget, the event is remove again:
@
void QApplicationPrivate::emitLastWindowClosed()
{
if (qApp && qApp->d_func()->in_exec) {
if (QApplicationPrivate::quitOnLastWindowClosed) {
// get ready to quit, this event might be removed if the
// event loop is re-entered, however
QApplication::postEvent(qApp, new QEvent(QEvent::Quit));
}
emit qApp->lastWindowClosed();
}
}
@EDIT: To explain (I'm sure @Gerolf, you know what this means, but now everybody reading this thread will), the close event on QApplication is one executed directly (which would be sendEvent()), but postponed until the eventLoop is run next, in order to give QWidget a chance to remove it again.