Callback for when a QWidget window is closed?
-
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?
-
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()).
-
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; };