1
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QDialog dlg;
    dlg.exec();
    return a.exec();
}

That's all my code, but when I close the window, The process isn't exit, it seems that drop in the loop a.exec().

Anudorannador
  • 189
  • 1
  • 1
  • 11
  • then don't do dlg.exec or a.exec – ratchet freak Aug 28 '14 at 09:46
  • 1
    Calling `dlg.show()` instead of `dlg.exec()` would be a possible solution. – thuga Aug 28 '14 at 09:48
  • but I want the dialog to be modal – Anudorannador Aug 28 '14 at 10:42
  • Call `dlg.exec()`, but don't call `a.exec()`. – thuga Aug 28 '14 at 10:51
  • It works, thanks! But would you like to tell me why? please. – Anudorannador Aug 28 '14 at 11:15
  • [`QApplication::exec`](http://qt-project.org/doc/qt-5/qapplication.html#exec) enters the main loop and stays there until [`QApplication::exit`](http://qt-project.org/doc/qt-5/qcoreapplication.html#exit) is called. When dealing with GUI apps, `QApplication::exit` is usually called when the last window is closed. But in your case, you close your last window before you call `QApplication::exec`, therefore calling it is unnecessary. If you do call it, there is nothing that calls `QApplication::exit` and that is why your application never closes. – thuga Aug 28 '14 at 11:29
  • you mean that, in my case, when `dlg.exec()` finished, `a.exit()` have been already called somewhere so that it will get stuck in an infinite loop when execute `a.exec()`? – Anudorannador Aug 28 '14 at 11:48
  • Not exactly. In your case, when you call `dlg.exec()` and it returns, `a.exit()` will not be called as `a.exec()` hasn't been called yet. After that you call `a.exec()` and there is nothing calling `a.exit()` so `a.exec()` never returns. – thuga Aug 28 '14 at 12:11

2 Answers2

4

Generally speaking, calling any exec is a bad idea, other than QCoreApplication::exec() or QDrag::exec(). The presence of exec() and waitForXxx() methods is an enticing trap for the unwary. Those methods are "easy" to use, but that ease comes at a price of hard to track bugs. Don't use them.

You should simply show the dialog:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QMessageBox msg;
    msg.setText("Hello");
    msg.addButton(QMessageBox::Close);
    msg.show();
    return a.exec();
}

If you wish to wait for the dialog to be accepted or rejected, you should use the dialog's clickedButton slot. QMessageBox has a long-standing bug that makes the accepted and rejected signals useless :(

// https://github.com/KubaO/stackoverflown/tree/master/questions/messagebox-show-25545652
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <functional>

[...]

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

   QMessageBox msg;
   msg.setText("Continue?");
   msg.addButton(QMessageBox::Yes);
   msg.addButton(QMessageBox::No);
   auto onClick = [&msg]() {
      auto role = msg.buttonRole(msg.clickedButton());
      if (role == QMessageBox::NoRole)
         QApplication::quit();
      if (role == QMessageBox::YesRole) {
         auto label = new QLabel("I'm running");
         label->setAttribute(Qt::WA_DeleteOnClose);
         label->show();
      }
   };
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
   QObject::connect(&msg, &QMessageBox::buttonClicked, onClick);
#else
   QObject::connect(&msg, SIGNAL(buttonClicked(QAbstractButton*)),
                    new FunctorSlot{onClick, &msg}, SLOT(call()));
#endif
   msg.show();
   return app.exec();
}
#include "main.moc"

For Qt 4, you need the following helper:

// Qt 4 only
struct FunctorSlot : public QObject {
   Q_OBJECT
public:
   std::function<void()> callable;
   template <typename Fun>
   FunctorSlot(Fun && fun, QObject * parent = {}) :
      QObject{parent}, callable{std::forward<Fun>(fun)} {}
   Q_SLOT void call() {
      callable();
   }
};
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

Possible solution:

QApplication a(argc, argv);
QDialog dlg;
QTimer::singleShot( &dlg, 0, SLOT(exec()) );
return a.exec();

It will work well. First - application event loop will be started. Then dialog event loop will be executed. After closing of dialog, both dialog and application loop will be finished. Application loop will be terminated automatically (by default), when last window is closed.

But, as noted by @thuga - there are no reason to call exec(). It is enough to call show() method.

Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
  • 1
    It is the presence of such hacks that gives me shivers. – Kuba hasn't forgotten Monica Aug 28 '14 at 12:59
  • Where do you see hacks? It is normal practice to call slots in such ways. For example - When you need to do some action (show window) after initialization of `QApplication`. And if you read my last sentence - you will see my opinion - use `show` instead of `exec`. – Dmitry Sazonov Aug 28 '14 at 13:10
  • Calling `QDialog::exec()` is a hack. An unnecessary one, at that. Most people who read such answers won't notice the last sentence. Your code should be correct, you shouldn't post bad code and then say "oh, btw, the above is wrong, do *this* instead". I'll upvote your answer and delete my answer as soon as the code gets fixed. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:13
  • I think that it to be `QTimer::singleShot(0, &dlg, SLOT(exec()));`. But how can I get the value returned from the slot `exec()`? – Anudorannador Aug 28 '14 at 13:17
  • @LazyCat If you need to get the result from a `QDialog`, without using the return value of `QDialog::exec`, you can call [`QDialog::result`](http://qt-project.org/doc/qt-5/qdialog.html#result). Or you can connect the [`QDialog::finished(int)`](http://qt-project.org/doc/qt-5/qdialog.html#finished) signal to a slot. – thuga Aug 28 '14 at 13:31
  • @KubaOber calling `QDialog::exec` - is correct way to show modal dialog. Why do you think that it is wrong? And using of `QTimer::singleShot()` is correct way to put some logic to workflow queue. – Dmitry Sazonov Aug 28 '14 at 13:37
  • @DmitrySazonov Calling `exec` can be dangerous, as pointed out [here](https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0). – thuga Aug 28 '14 at 13:39
  • Because the event loop is running and events will be delivered to other objects in your application. The code that shows the dialog can be re-entered, `deleteLater` methods are no-ops until the dialog is dismissed, etc. It leads to problems that become harder to debug the larger the application gets. It was a mistake to have those functions in the API. They are the convenience methods of the worst sort. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:39
  • 2
    Besides, that `singleShot` is an anti-idiom as well. IMHO the `singleShot` method should assert on a zero timeout. You should use `QMetaObject::invokeMethod(&dlg, "exec", Qt::QueuedConnection)`. It makes it obvious what behavior is sought. The zero-duration single shot timer is an obfuscation technique that should be fought with fire. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:42
  • It is a problem of developers, not of framework, if they don't understand how modal windows are working. There are same thing in Windows event loop, for example - when you are calling `MessageBox` function - inner event loop is executed. Imo, any modal windows - is evil. – Dmitry Sazonov Aug 28 '14 at 13:42
  • @DmitrySazonov Sure, and that's why we don't suggest such things to newbies. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:43
  • Agree about `singleShot` and `invokeMethod`. I just tried to answer original question. I think our discussion should be moved outside from this question, because it related to answering rules. Some people on stackoverflow just gives answers, and some people are trying to teach newbies. I belong to first part, because I belive that own mistakes - is best expirience. So I provided an answer + note about what is wrong. (Sorry for bad English) – Dmitry Sazonov Aug 28 '14 at 13:46
  • Thanks for all. @DmitrySazonov @KubaOber @thuga I think I should change my mind. re-design my program so that no modal window would be used. I think `QDialog::result` is very good function and very useful! – Anudorannador Aug 28 '14 at 14:02