QT quit does not call MainWindow destructor - c++

In my application I have a tray icon and so I overrode closeEvent so that the application "minimizes" when certain things happen. However, I do expect that upon pressing exit, that the application will completely exit. However, after overriding closeEvent and calling the function quit(), it seems to bypass the MainWindow destructor, where I have some code.
What am I missing in closeEvent to properly close the application so that the destructor of MainWindow is called, as is the case when closeEvent isn't overriden?
I've tried using
QMainWindow::closeEvent(event);
and a few other things, but the destructor is never called.
My close event implementation is:
void MainWindow::closeEvent(QCloseEvent * event)
{
if(m_closeCompletely == false)
{
if (trayIcon->isVisible())
{
QMessageBox::information(this, tr("Hello"),
tr("The program will keep running in the "
"system tray. To terminate the program, "
"choose <b>Quit</b> in the context menu "
"of the system tray entry."));
}
}
else
{
event->accept();
}
}

The WA_DeleteOnClose attribute needs to be set to call the destructor on a close event, otherwise a widget or window is just hidden from view. Add the following to the class constructor:
this->setAttribute(Qt::WA_DeleteOnClose);
This will cause the destructor to be called on a close event.

It all depends on where and how you allocated the objects you are using. If you create the QApplication and QMainWindow on the heap then they will lurk around until the OS cleans up the memory. Sending a close() to a QWidget (and QMainWindow is one) will only close it .. hide it visually from the user. You can show() later on, close() does not destruct it.
You could use something like this:
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
MyMainWindow mw;
mw.show();
int rc = app.exec();
// optional: do_cleanup();
return rc;
}
app and mw are constructed on the stack. They will be destructed in reverse order when the app returns from the exec() call (usually when you send quit() to the QCoreApplication::instance()). You can even provide a plain cleanup function, no need to put something into the destructor of the UI.

Remember that all the Application Resources are hold by the QApplication;
Once your MainWindow is closed, it just be close, aka hide from the screen;
I wonder why you want to destructor your MainWindow, it seems that you've made a system tray entry, so my suggestion is just to hide the MainWindow but to destructor, cause there is a system tray, you may want to click it to show the MainWindow.
Hope to Help. : )

I just ran into this same problem and it was because I was running an infinite loop that never allowed the application to close.
forever
{
_runGameLoop();
}
I had to make the loop end when MainWindow closed
while(!M->isClosed())
{
_runGameLoop();
}
And of course I had to implement isClosed()
bool MainWindow::isClosed()
{
return _isClosed;
}
void MainWindow::closeEvent(QCloseEvent*)
{
_isClosed = true;
}

Related

QMainWindow wont open in conditional block

Before opening a QMainWindow a login occurs, and there are 3 possible results, an error, a teacher and a user, if the login result is a user must open a QMainWindow pro user, if he is a teacher his QMainWindow.
LoginDialog login;
login.exec();
switch(login.result()){
case LoginResult::NONE:
qDebug() << "None";
break;
case LoginResult::PROF:
qDebug() << "Prof";
break;
case LoginResult::USER:
{
MainWindow w;
w.show();
}
break;
case LoginResult::ERROR:
Database::Terminate();
return -1;
}
The problem is that inside the case the window only opens for less than a second and automatically closes, outside the switch, the window shows and stays as it should.
Why does this occur?
The main window object goes out of scope at the end of the block, and is destroyed. You need to create it with new to prevent this:
case LoginResult::USER:
{
auto *w = new MainWindow;
w->setAttribute(Qt::WA_DeleteOnClose); // to prevent memory leak
w->show();
}
Above pointer variable w goes out of scope, but since it is plain raw pointer, this doesn't delete the actual object. Setting the object to delete itself when it is closed is a QWidget feature, and is one way to make sure the main window gets cleanly deleted. Not sure if it is a good way for your application, but you can start with it, and then do something else later if needed.

Interaction between QMainWindow class and QDialogue class

I am developing a GUI app. I have a main window(QMainWindow) which pops up when the app is executed.
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This window has some buttons. When I click a button here, another window which is of QDialog is popped up. The logic of the button is
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
lib.setModal(false);
lib.exec();
}
Question: I have a socket in the QMainWindow class, whenever I receive a message there I need to send it to the QDialogue window and display it in a QLineEdit. With the above logic, even after I set QDialogue to nonmodal, I am not able to interact with the QMainWindow when the QDialogue window is open. I tried lib.show() instead of lib.exec(), but with this, when I click the button the QMainWindow class the QDialogue window does not pop up.
Please advice me what is the best method to communicate from the background window the the foreground window?
Your code can looks like
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( letsChangeLineEdit( QString ) ) ) );
lib.setModal(false);
lib.exec();
}
Of course you should implement your signal and slot. QString in signal slot arguments is target string to show in dialog.
Hope that will clear for you.
In your MainWindow::on_InsertButton_pressed()slot you are creating a local variable libinsert lib which gets destroyed when it gets out of scope, i.e. when the slot execution finishes. If you use lib.exec() the execution waits for the dialog execution to return so you see the dialog, but can't interact with the main window until you close it. When you use lib.show(), your dialog is shown, execution continues and exits the slot right away, at which point the lib gets destroyed and you don't see it any more (depending on your configuration this may be fast enough that it looks like it newer gets shown in the first place).
One solution is to have a pointer to your dialog as a member of MainWindow class (add libinsert* lib; in MainWindow.h), and initialize it in your Mainwindow constructor like this:
`MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
lib = new libinsert(this);
lib->setModal(false);
}`
Then in MainWindow::on_InsertButton_pressed() you only need to call QWidget::show() method, i.e. like this:
void MainWindow::on_InsertButton_pressed()
{
lib->show();
}
Now, if what you are trying to achieve is to have a dialog pop-up every time the message is received , and display the message, first you will need to have a public method(or slot) in your libinsert class which updates the text in your QLineEdit, for example
void libinsert::onMessageReceived(QString message)
{
// Update the text and show the dialog to avoid having to call show()
// explicitly from MainWindow.
ui->lineEdit->setText(message);
show();
}
and then either call this method directly, when you receive the message, with:
lib->onMessageReceived(message);
or connect a signal, emitted when message is received, to it with something like this (in MainWindow constructor):
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( onMessageReceived( QString ) ) ) );

C++ application does not kill all processes on exit

This is my main.cpp which starts the mainwindow:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabWindow w;
w.show();
return a.exec();
}
Even with the a.connect(...) I do not understand why myApplication.exe still runs after I close the mainwindow. Any suggestions on how I can fully end all processes after the quit button is clicked?
EDIT: The Qt Documentation says this:
We recommend that you connect clean-up code to the aboutToQuit() signal, instead of putting it in your application's main() function. This is because, on some platforms, the QApplication::exec() call may not return.
There's nothing wrong with your code. And your connect doesn't do anything.
Unless you call QGuiApplication::setQuitOnLastWindowClosed(true) somewhere, application should exit when the last window is closed. Probably you block event loop somewhere in your window code.
Thanks to the comment posted by #ratchetfreak in my question, I figured out where the problem was.
In my MainWindow, I started a worker thread which was not terminated and thus still persisted as a process after the application was closed. In order to fix this, I registered the close event and kept track of the existence of the thread - i.e. basically, ignored the closeEvent until the thread was deleted as well.
void MainWindow::closeEvent(QCloseEvent *event)
{
if (workerThreadExists) {
// Gracefully exiting all tasks inside the worker thread
while (workerThreadExists){
event->ignore();
}
event->accept();
}
}
...and for me workerThreadExists is just a BOOLEAN that is set to true once the thread is created and then it is set to false when the thread is deleted. Hope this helps!
You should have something like this:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabWindow w;
w.show();
return a.exec();
}
// Do quit in your TabWindow:
TabWindow::TabWindow()
: public QWidget(NULL)
{
connect( ui->myQuitButton, SIGNAL( clicked() ), qApp, SLOT( quit() ) );
}
just replace
return a.exec();
with
return 0;
That should end all processes which is associated with that program.

How to do things upon quitting a Qt app

To my understanding, the following will execute an application which will exit when all associated windows are closed.
int main(int argc, char *argv[])
{
QApplication cal(argc, argv); //Application instance
MainWindow window; //Create window in the stack
window.show(); //Show the window
return cal.exec(); //Execute event loop
}
This will also quit said application.
quitButton = new QPushButton("Quit", this);
connect(quitButton, SIGNAL(clicked()), QApplication::instance(), SLOT(quit()));
How should I go about cleaning up (closing hardware connections and libraries etc.) so that I can be sure everything that needs to happen happens no matter how the app it exited, nor at what point in execution it is exited?
I will have a library open throughout the whole app, and a connection to a USB device at most times. I am able to close this connection easily if the process runs its course, but I want the connection to be closed safely if somebody decides to hit quit or close all the windows before it is done.
There is a virtual function called closeEvent for a QWidget (see Documentation, which is called when the widgets close() operation is called.
Alternatively, if you don't want to have your own Base widget, you can hook yourself to the signal QApplication::lastWindowClosed (see here). It is emitted when the last visible window of your application closes.
You can do the following:
int main(int argc, char *argv[])
{
QApplication cal(argc, argv);
// Allocate resources
int ret = cal.exec(); // start the event loop and wait until it quits
// Free the resources
return ret;
}
The proper C++ way of doing it is:
Ensuring that all of your object instances get destructed.
Ensuring that any resources you hold are held via RAII.
Then you don't need to do anything special. As the objects get destructed when main returns, things get cleaned up.
For example:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QObject foo;
QObject * bar = new QObject(&app);
Widget w;
w.show();
return app.exec();
}
Here, both foo and bar are properly destructed before main returns. Now suppose you hold some non-memory resources, like a file:
class Worker : public QObject {
QFile m_file;
...
void doSomething() {
if (m_file.open()) {
...
}
// No file closing!
}
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker foo;
Widget w;
w.show();
return app.exec();
}
Again, the file gets closed before main returns.
That's how RAII works, in a nutshell. If you have your own classes that wrap OS or other resources, simply ensure that such resources are freed when the classes get destructed - and that the classes are always destructible (it's always safe to destruct them). My pet peeve is QThread - it breaks this expectation, and you have to implement a wrapper to fix it.
For resources that you need to allocate on the heap, you're supposed to be using smart pointers or QObject memory management.
There is a QApplication signal that you can use for cleaning up before the application quits:
QObject::connect(&app, SIGNAL(aboutToQuit()), &MyCleaner, SLOT(cleanUp()));
Have fun!

Closing a QMainWindow on startup?

I have a Qt application that uses a QMainWindow-derived class for the main UI. On startup I want to make some security checks and, if they fail, display a message to the user and close the main window. Currently I make these checks in the QMainWindow constructor, but if I call the close method, nothing happens and the application continues to run. For example:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
...
if (checkFails())
{
QMessageBox::warning(this, tr("Error"), tr("You cannot run this app"));
// This has no effect
close();
}
}
Alternatively I could make the checks in the main function but then I lose the ability to display a language-specific message box (the tr macro only works in a QObject-derived class by the looks of things.)
Any ideas on how to close the main window on startup or make the tr macro work outside of a QObject derived class?
The event loop needs to be running before you can successfully close the main window. Since you probably first construct a window, and then start the event loop the close() call has no effect. Try the following solution instead:
QTimer::singleShot(0, this, SLOT(close()));
The QTimer::singleShot() will fire as soon as an event loop has been started, and subsequently calls the close() method on your application main window.
The above solution will probably cause your main application window to be visible for a short period of time, causing unwanted flickering.
A cleaner solution should perform the security checks prior to constructing the main window. Since tr() is also available as a static method on QObject, this can be done from the main function.
Most applications start up in three steps: 1) construct the window; 2) show the window; 3) start the event loop. You can make steps 2 and 3 conditional on the success of step 1 by adding a flag, whose value is set by the window constructor, to the window class:
Window class:
class myMainWindowClass : public QMainWindow
{
Q_OBJECT
public:
myMainWindowClass()
: isFinished_(false) { if (error) isFinished_ = true; } // constructor
bool isFinished() const { return isFinished_; }
private:
bool isFinished_;
}
Application code:
int main()
{
myMainWindowClass main_window(); // Step 1
// Finish early if isFinished flag is set
if (main_window.isFinished())
return 0;
main_window.show(); // Step 2
return a.exec(); // Step 3
}
This should also avoid any flicker as the application will end before the window is show()n.
tr is a public static member of QObject. You should be able to call QObject::tr("Error") in your main function.
Have you tried first hide()ing the window (this should occur anyway when close() is called) to see if this then allows close() to destroy the window.
If this does not work, you could always try destroy(true, true)ing the window along with any sub-windows.