[SOLVED] Correctly exiting from a Qt GUI application with multiple windows/widgets



  • Hi,
    I have a problem to correctly exit my application.
    There are 2 "modes" in which my application runs, "GUI mode" and cmdline mode. The mode is switched by passing an argument to the application at startup.

    main.cpp:

    @
    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    MainWindow w;
    if (argc>1 && strcmp(argv[1], "--nogui") == 0)
    {
    w.hide();
    }
    else
    {
    w.show();
    }
    return a.exec();
    }
    @

    From the MainWindow a second window ("debugPtr" in line 15) is created which is a QWidget containing just a QTextEdit (for debug output).

    In GUI mode everything works as expected - after closing both windows the application exits.
    However, the cmdline mode does not exit properly: cmdline mode is non-interactive, i.e. it conducts some calculations based on settings from a config file and is supposed to quit after that (see mainwindow.cpp, l. 17-22). However, that never happens. It basically "hangs" at the "QApplication::quit()" command. I assumed that it would close all windows of the applications, but it seems it doesn't.
    While investigating this I also tried to make the 2nd window a child of the MainWindow, however that didn't work well, because then the application crashes in the destructor of the MainWindow. Then I commented "delete debugPtr;" in the destructor since it crashed there, also because I understood the documentation in a way that the parent window takes care of deleting its children; but now the destructor crashed in the following line, "delete ui;" so there is something more fundamental going wrong in my approach, I suppose. I am probably misunderstanding someting in the concept of child windows. So I switched back and the 2nd window is now not a child of the MainWindow anymore. Shouldn't it close nevertheless when the application is asked to quit? Is maybe "QApplication::quit()" not the right way? (I think I found it on stackoverflow.com.)

    Note: The 2nd window is created, but not shown, in cmdline mode as well: Within that window's code the output is then done or not done based on the "application mode" in order not to have to check for the application mode at each output of text to this window. This is the reason for creating the window also in cmdline mode.

    Thanks in advance for any suggestions and ideas!

    The rest of the discussed code is:
    (I hope I did not forget any, if so please give me a hint.)

    mainwindow.cpp:

    @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);

    QString appArgv = qApp->arguments().last(); // Check if an argument was passed at ap startup.
    isCmdLine = false;
    // if there is more than one argument passed to the app, use the last. If it is "--nogui", then start the application in commandline mode
    if (appArgv == "--nogui")
    {
    isCmdLine = true;
    }

    debugPtr = new DebugDialog(0, isCmdLine); // Create the 2nd window. 0 means no parent, the 2nd argument passes the mode.

    if (isCmdLine == true)
    {
        if (ReplicateRunsAutomatic() == true) // Non-interactive calculations in cmdline mode.
        {
            qDebug() << "Calculations finished successfully. Exiting.";
            QApplication::quit();  // <- Is supposed to exit the application.
        }
    }
    

    else
    {
    // GUI mode stuff
    }
    }

    MainWindow::~MainWindow()
    {
    delete debugPtr;
    delete ui;
    }
    @

    debugdialog.h:

    @class DebugDialog : public QDialog
    {
    Q_OBJECT

    public:
    explicit DebugDialog(QWidget *parent = 0, bool isCmdLine = false);
    ...
    }
    @


  • Lifetime Qt Champion

    Hi,

    Why not something like that:

    @
    QApplication a(argc, argv);
    if (argc>1 && strcmp(argv[1], "--nogui") == 0)
    {
    MainWindow w;
    w.show();
    return a.exec();
    }
    else
    {
    if (ReplicateRunsAutomatic() == true) // Non-interactive calculations in cmdline mode.
    {
    qDebug() << "Calculations finished successfully. Exiting.";
    return 1;
    }
    return 0;
    }
    @

    Unless your calculations make use of Qt's features, there's no need to start the application. Also it's not the GUI's responsibility to handle that, thus the separation is cleaner.

    Hope it helps



  • I would have to read a bit for which of the Qt libraries that I use I actually need a QApplication. Of course I use Qt classes like QString and also containers like QList; maybe for those I do not need a QApplication (not a Qt expert yet :-) ).
    And for the debug output window I could use a class which contains a QWidget instead of directly inheriting from QWidget. Then the window would only be created in GUI mode but not in cmdline mode and probably a QApplication would be unnecessary. The calls to the debug output would still go to this object but would be filtered out there.

    I will try that, thank you :-)
    Any ideas regarding my parent/children problem?


  • Lifetime Qt Champion

    What exact error are you getting ?


  • Moderators

    For command line mode I use QCoreApplication and for gui QApplication.

    The reason it doesn't exit as SGaist said is that you are calling a.exec() at the end. Skip that like he showed you when in command line mode and you should be good.

    So for a simple command line app it would be:

    @
    #include <QCoreApplication>

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

    // do whatever you want here, process arguments, calculate things, etc

    return 0;
    }
    @

    No need to ever go into app.exec() unless you need an event loop. If that was the case you could then call app.quit() somewhere in your CLI processing section. You would need to start whatever you wanted with a QTimer::singleShot() so you could block on the app.exec() but still handle your CLI stuff since there would be no GUI interface.

    Oh and if you are in CLI mode I definitely wouldn't create the widget or a QApplication since those are GUI things. That would make it not a true CLI. It would cause issues if you tried to run it in a headless situation like a linux terminal with no X. It wouldn't start since it would need X windows to create (and then hide) the widget.

    So separate your code into GUI/CLI sections and only create GUI objects in the GUI section.



  • Hi,

    [quote author="SGaist" date="1405724470"]What exact error are you getting ?[/quote]

    I assume you refer to the crash in the destructor? It was a segment violation, and with a debugging build it ended up in a Qt library I think, at least not in my source code. However, I just tried it again (i.e. set the MainWindow as parent of the 2nd window), and it does not crash anymore in the destructor, for whatever reason ... So that problem is "solved" :-)

    I will try to restructure my project in a way that no QApplication is needed in case of cmdline mode so that I can use a QCoreApplication then.

    Thanks for all your help so far!


  • Lifetime Qt Champion

    On a side note, when you create a new widget you should put the parent parameter at the end of the list. Doing so you will follow the standard Qt coding style and it won't hurt your memory muscle when using your widget ;)



  • Will do :-)



  • I followed your suggestions and now in cmdline mode only a QCoreApplication is created, only for GUI mode a QApplication is created. Works nicely and finishes as expected after the calculations in cmdline mode. At first, in GUI mode the 2nd window didn't close when closing the MainWindow, although I am setting the parent to MainWindow when creating the 2nd window. But after setting the Qt::WA_DeleteOnClose attribute of the window, the window now closes as well when closing the MainWindow.

    Thanks SGaist and ambershark!


  • Moderators

    Nice. Glad you got it all worked out.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.