Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to properly exit GUI (with QT Widgets) application
Forum Updated to NodeBB v4.3 + New Features

How to properly exit GUI (with QT Widgets) application

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 6 Posters 634 Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Art Greenberg
    wrote on last edited by
    #1

    I've written a GUI app using Qt Creator and Qt 6.9.1, and I'm running it on Linux. To close the application, I have a File->Exit menu action connected to QCoreApplication::quit using a queued connection as suggested in the documentation.

    exitAction = new QAction ( "E&xit", this ) ;
    connect ( exitAction, &QAction::triggered, this, &QCoreApplication::quit, Qt::QueuedConnection ) ;
    

    So I can perform some cleanup and store the application's configuration at the close, I've written a closeEvent handler:

    void K4Remote::closeEvent ( QCloseEvent *event )
    {
        Configuration::WriteConfiguration () ;
        m_audioDecoder->Stop () ;
        m_audioEncoder->Stop () ;
        m_pttSwitch->Close () ;
        event->accept () ;
    }
    

    When I run the application in QtCreator and select File->Exit, I see the message in the application output window that says the application finished successfully.

    However, it seems as if the call to WriteConfiguration() is not fully executed. I have put qDebug() statements at the start and end of WriteConfiguration(), and I see both output as expected. However, the several QSettings::setValue() calls inside don't seem to be effective - that is, nothing is written to the config file, and its modification time stamp is unchanged. I have not verified that the other calls within the close event handler have run successfully, but I have not detected an issue that would result from them failing.

    On the other hand, if I exit the application by clicking on the close box ("X" at the extreme right end of the title bar), the application exits with a "terminated abnormally" status, and the config file IS updated.

    My questions:

    (1) Why does WriteConfiguration() fail when terminating the application via File->Exit?

    (2) Why does WriteConfiguration() succeed when terminating the application via the close box?

    (3) Why does the application return "terminated abnormally" status when terminated using the close box?

    Thank you.

    jsulmJ 1 Reply Last reply
    0
    • A Art Greenberg

      I've written a GUI app using Qt Creator and Qt 6.9.1, and I'm running it on Linux. To close the application, I have a File->Exit menu action connected to QCoreApplication::quit using a queued connection as suggested in the documentation.

      exitAction = new QAction ( "E&xit", this ) ;
      connect ( exitAction, &QAction::triggered, this, &QCoreApplication::quit, Qt::QueuedConnection ) ;
      

      So I can perform some cleanup and store the application's configuration at the close, I've written a closeEvent handler:

      void K4Remote::closeEvent ( QCloseEvent *event )
      {
          Configuration::WriteConfiguration () ;
          m_audioDecoder->Stop () ;
          m_audioEncoder->Stop () ;
          m_pttSwitch->Close () ;
          event->accept () ;
      }
      

      When I run the application in QtCreator and select File->Exit, I see the message in the application output window that says the application finished successfully.

      However, it seems as if the call to WriteConfiguration() is not fully executed. I have put qDebug() statements at the start and end of WriteConfiguration(), and I see both output as expected. However, the several QSettings::setValue() calls inside don't seem to be effective - that is, nothing is written to the config file, and its modification time stamp is unchanged. I have not verified that the other calls within the close event handler have run successfully, but I have not detected an issue that would result from them failing.

      On the other hand, if I exit the application by clicking on the close box ("X" at the extreme right end of the title bar), the application exits with a "terminated abnormally" status, and the config file IS updated.

      My questions:

      (1) Why does WriteConfiguration() fail when terminating the application via File->Exit?

      (2) Why does WriteConfiguration() succeed when terminating the application via the close box?

      (3) Why does the application return "terminated abnormally" status when terminated using the close box?

      Thank you.

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Art-Greenberg We don't know how Configuration::WriteConfiguration is implemented. Do you call QSettings::sync()?

      (3) Use the debugger to see what exactly happens.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • C Offline
        C Offline
        ChrisW67
        wrote on last edited by ChrisW67
        #3

        This simple example, modelled on your problem, calls closeEvent either way for me (Linux, Qt 6)

        #ifndef MAINWINDOW_H
        #define MAINWINDOW_H
        
        #include <QMainWindow>
        
        class MainWindow : public QMainWindow
        {
            Q_OBJECT
        
        public:
            MainWindow(QWidget *parent = nullptr);
            ~MainWindow();
        
            // QWidget interface
        protected:
            virtual void closeEvent(QCloseEvent *event) override;
        };
        #endif // MAINWINDOW_H
        
        #include "mainwindow.h"
        #include <QDebug>
        #include <QCloseEvent>
        #include <QMenuBar>
        #include <QMenu>
        #include <QAction>
        
        MainWindow::MainWindow(QWidget *parent)
            : QMainWindow(parent) {
            QAction *exitAction = new QAction ( "E&xit", this ) ;
            connect ( exitAction, &QAction::triggered, this, &QCoreApplication::quit, Qt::QueuedConnection ) ;
        
            QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
            fileMenu->addAction(exitAction);
        }
        
        MainWindow::~MainWindow() { }
        
        void MainWindow::closeEvent(QCloseEvent *event) {
            qDebug() << Q_FUNC_INFO;
            event->accept();
        }
        
        #include "mainwindow.h"
        #include <QApplication>
        
        int main(int argc, char *argv[])
        {
            QApplication a(argc, argv);
            MainWindow w;
            w.show();
            return a.exec();
        }
        

        With regard to QSettings. Changes are not immediately (synchronously) written to disk. To quote the docs, " For efficiency, the changes may not be saved to permanent storage immediately. (You can always call sync() to commit your changes.)" With the QCoreApplication::quit() path, the event loop might exit before the sync to disk happens. (You may have this race condition either way.)
        Are you calling QSettings::sync() in your WriteConfiguration routine?

        The closeEvent for a widget is called when you explicitly call QWidget::close() or the user takes an action to close the widget (e.g. your application's main window close button). This is exactly what is happening in case 2.

        jeremy_kJ 1 Reply Last reply
        1
        • C ChrisW67

          This simple example, modelled on your problem, calls closeEvent either way for me (Linux, Qt 6)

          #ifndef MAINWINDOW_H
          #define MAINWINDOW_H
          
          #include <QMainWindow>
          
          class MainWindow : public QMainWindow
          {
              Q_OBJECT
          
          public:
              MainWindow(QWidget *parent = nullptr);
              ~MainWindow();
          
              // QWidget interface
          protected:
              virtual void closeEvent(QCloseEvent *event) override;
          };
          #endif // MAINWINDOW_H
          
          #include "mainwindow.h"
          #include <QDebug>
          #include <QCloseEvent>
          #include <QMenuBar>
          #include <QMenu>
          #include <QAction>
          
          MainWindow::MainWindow(QWidget *parent)
              : QMainWindow(parent) {
              QAction *exitAction = new QAction ( "E&xit", this ) ;
              connect ( exitAction, &QAction::triggered, this, &QCoreApplication::quit, Qt::QueuedConnection ) ;
          
              QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
              fileMenu->addAction(exitAction);
          }
          
          MainWindow::~MainWindow() { }
          
          void MainWindow::closeEvent(QCloseEvent *event) {
              qDebug() << Q_FUNC_INFO;
              event->accept();
          }
          
          #include "mainwindow.h"
          #include <QApplication>
          
          int main(int argc, char *argv[])
          {
              QApplication a(argc, argv);
              MainWindow w;
              w.show();
              return a.exec();
          }
          

          With regard to QSettings. Changes are not immediately (synchronously) written to disk. To quote the docs, " For efficiency, the changes may not be saved to permanent storage immediately. (You can always call sync() to commit your changes.)" With the QCoreApplication::quit() path, the event loop might exit before the sync to disk happens. (You may have this race condition either way.)
          Are you calling QSettings::sync() in your WriteConfiguration routine?

          The closeEvent for a widget is called when you explicitly call QWidget::close() or the user takes an action to close the widget (e.g. your application's main window close button). This is exactly what is happening in case 2.

          jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by
          #4

          @ChrisW67 said in How to properly exit GUI (with QT Widgets) application:

          With regard to QSettings. Changes are not immediately (synchronously) written to disk. To quote the docs, For efficiency, the changes may not be saved to permanent storage immediately. (You can always call sync() to commit your changes.) With the QCoreApplication::quit() path, the event loop might exit before the sync to disk happens. (You may have this race condition either way.)
          Are you calling QSettings::sync() in your WriteConfiguration routine?

          I edited the quoting of the QSettings documentation to make it clear that the comment about a race is not from the documentation. Syncing through invocation of the QSettings destructor doesn't depend on the event loop.
          QSettings::~QSettings():

          QSettings::~QSettings()
          {
              Q_D(QSettings);
              if (d->pendingChanges) {
                  // Don't cause a failing flush() to std::terminate() the whole
                  // application - dtors are implicitly noexcept!
                  QT_TRY {
                      d->flush();
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          1 Reply Last reply
          0
          • JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @ChrisW67 , @jeremy_k
            I read what you both say. Personally I have never had a problem with QSettings or its writing to file, but then again I have never tried to save an extra setting during a closeEvent() or similar. What you say worries me: the user should not have to worry about syncing/flushing just as, say, program is exiting. During my program exit code I have "no idea" whether the rest of the code might have saved a setting, and I should not have to know. On that basis there might be a hundred other things which I ought to deal with, just in case. Doesn't either exiting the top-level Qt event loop cause flush, or the destructor you show should be hit and that will flush?

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Art Greenberg
              wrote on last edited by
              #6

              Thanks to all. I've resolved the issue. Here is what I found:

              • My declaration for closeEvent() did not look like the declaration in the example from @ChrisW67.

              • I did not call QSettings::sync() in my implementation of WriteConfiguration(). Thanks @ChrisW67.

              • The implementation of m_pttSwitch->Close() left a timer running and that caused the abnormal termination. Thanks to @jsulm for suggesting running under the debugger.

              After those corrections, my application closes normally and updates the configuration file as expected with either selecting File->Exit or by clicking the close box.

              I think @JonB maybe has a point. The Qt platform takes care of so much for us that we might expect QSetting::sync() to be done for us. The documentation is clear enough, but I missed it until it was pointed out.

              Art

              JonBJ 1 Reply Last reply
              1
              • A Art Greenberg

                Thanks to all. I've resolved the issue. Here is what I found:

                • My declaration for closeEvent() did not look like the declaration in the example from @ChrisW67.

                • I did not call QSettings::sync() in my implementation of WriteConfiguration(). Thanks @ChrisW67.

                • The implementation of m_pttSwitch->Close() left a timer running and that caused the abnormal termination. Thanks to @jsulm for suggesting running under the debugger.

                After those corrections, my application closes normally and updates the configuration file as expected with either selecting File->Exit or by clicking the close box.

                I think @JonB maybe has a point. The Qt platform takes care of so much for us that we might expect QSetting::sync() to be done for us. The documentation is clear enough, but I missed it until it was pointed out.

                Art

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by
                #7

                @Art-Greenberg said in How to properly exit GUI (with QT Widgets) application:

                I think @JonB maybe has a point. The Qt platform takes care of so much for us that we might expect QSetting::sync() to be done for us

                If @jeremy_k is saying QSettings::~QSettings() has the code he shows then shouldn't it write to file when/assuming that is called?

                1 Reply Last reply
                0
                • A Art Greenberg has marked this topic as solved on
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Hi,

                  Out of curiosity, why do that in the closeEvent rather than the class destructor ?

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved