Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved Callback for when a QWidget window is closed?

    General and Desktop
    3
    5
    502
    Loading More Posts
    • 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.
    • R
      Rarrum last edited by

      I'm learning Qt, and have a simple application with two windows. I'm trying to write code that gets called when either window is closed, but I can't get Qt to call me when this happens. I've distilled the problem down to this sample app:

      #include <vector>
      #include <iostream>
      
      #include <QApplication>
      #include <QWidget>
      #include <QPushButton>
      #include <QObject>
      
      int main(int argc, char **argv)
      {
          std::cout<<"Meow Start"<<std::endl;
      
          QApplication app(argc, argv);
          std::vector<std::unique_ptr<QWidget>> allWindows;
      
          {
              std::unique_ptr<QWidget> window = std::make_unique<QWidget>();
              window->resize(320, 240);
              window->setWindowTitle("One");
              window->show();
      
              QObject::connect(window.get(), &QWidget::close, [&]()
              {
                  std::cout<<"One close"<<std::endl;
              });
      
              allWindows.emplace_back(std::move(window));
          }
      
          {
              std::unique_ptr<QWidget> window = std::make_unique<QWidget>();
              window->resize(320, 240);
              window->setWindowTitle("Two");
              window->show();
      
              QObject::connect(window.get(), &QWidget::close, [&]()
              {
                  std::cout<<"Two close"<<std::endl;
              });
      
              allWindows.emplace_back(std::move(window));
          }
      
          std::cout<<"Before exec"<<std::endl;
          int execResult = app.exec();
          std::cout<<"After exec"<<std::endl;
          return execResult;
      }
      

      When I run this, I get two windows, but neither close event is called. The output leads me to believe that "close" is not the right thing to use here:

      xmake run
      
      Meow Start
      QObject::connect: signal not found in QWidget
      QObject::connect: signal not found in QWidget
      Before exec
      After exec
      

      I've tried using the "destroyed" connect call instead, which doesn't produce the signal not found errors, and is closer to working:

              QObject::connect(window.get(), &QWidget::destroyed, [&]()
              {
                  std::cout<<"One destroyed"<<std::endl;
              });
      

      However the code isn't called when I close any individual window. But both callbacks are called after both windows are closed:

      xmake run
      
      Meow Start
      Before exec
      After exec
      One destroyed
      Two destroyed
      

      From some searching, I heard about the DeleteOnClose attribute:

      window->setAttribute(Qt::WA_DeleteOnClose, true);
      

      This gets the callback to work, however the app crashes upon closing:

      xmake run
      
      Meow Start
      Before exec
      Two destroyed
      One destroyed
      After exec
      error: execv(C:\vc\EasyAutoTracker\test\build\windows\x64\release\test.exe) failed(-1073741819)
      

      I wondered if this was causing the actual QWidget object to be deleted prematurely, so I removed my app's code to free it, which solved that problem. However I'd prefer to handle that deletion myself when other code is ready for that object to actually go away.

      Is there a better way to get this "the user closed the window" callback working without setting the DeleteOnClose attribute?

      1 Reply Last reply Reply Quote 0
      • C
        ChrisW67 last edited by

        QWidget::close() is not signal.
        You want to subclass QWidget::closeEvent() and emit a signal of your own, or do whatever processing you intended there.

        The destroyed() signal will be emitted during destruction of the QWidget instance i.e. when delete is called on the object. In idiomatic Qt programming this would happen when the widget's parent is destroyed (most of the time) or when the stack-based widget goes out of scope (the program's top-level widgets in main()).

        1 Reply Last reply Reply Quote 1
        • R
          Rarrum last edited by

          I was hoping to avoid having to subclass it, but I'll try that route. Thanks!

          1 Reply Last reply Reply Quote 0
          • R
            Rarrum last edited by

            Can confirm, this did the trick. Instead of using QWidget directly, I use:

            #include <functional>
            
            #include <QWidget>
            #include <QtGui/QCloseEvent>
            
            class ClosableQWidget: public QWidget
            {
            protected:
                inline void closeEvent(QCloseEvent *event) override
                {
                    if (closeCallback)
                        closeCallback();
                }
            
            public:
                std::function<void()> closeCallback;
            };
            

            Then I can hookup a callback similar to the connect pattern, with:

                window->closeCallback = [&]()
                {
                    std::cout<<"Window was closed"<<std::endl;
                };
            
            JonB 1 Reply Last reply Reply Quote 0
            • JonB
              JonB @Rarrum last edited by

              @Rarrum
              Indeed. Unlike connecting to signals, accessing Qt ...Event() methods requires subclassing. If you do not wish to do that, be aware that you can use QObject::eventFilter() to avoid having to subclass.

              1 Reply Last reply Reply Quote 1
              • First post
                Last post