How to keep the application running when the last window is closed? - c++

I'm working on a very basic GUI project in Qt (using c++) and want to be able to close the main window in my program without the program quitting all the way. By default, it will exit when the main window is closed. How to prevent that?

Set the QApplication::quitOnLastWindowClosed property to false:
qApp->setQuitOnLastWindowClosed(false);

If you still need your window to exist, you would probably like to reimplement your closeEvent method like this:
void MainWindow::closeEvent(QCloseEvent *event)
{
hide();
event->ignore();
}
or use QGuiApplication::setQuitOnLastWindowClosed(false)
If you want to perform some pre-exit operation as saving settings, connect some slot doing what you want to QCoreApplication::aboutToQuit()

Related

Qt DLL in MFC app - How to make QDialog *really* modal?

At the moment I am developing a Windows DLL with Qt 5.9.2 (MSVC 2015 compiler), which should be loaded by an existing, commercial MFC application. Upon request of this application a modal instance of QDialog should be displayed.
Since QApplication::exec() would block the entire application, I "simulate" the event loop using the following code:
void Core::createQApplicationInstance()
{
// Check, if there's already a 'QApplication' instance running (unlikely)
if (!QApplication::instance())
{
int argc = 1;
// Create a new 'QApplication' instance
m_app = new QApplication(argc, nullptr);
// Create a 'QTimer' instance to call 'processEvents' periodically:
// We can't run 'm_app->exec()' because it would block everything,
// so we'll use this 'hacky-whacky' method here
m_timer = new QTimer;
// Connect the timer's timeout to the app's 'processEvents' via a lambda
QObject::connect(
m_timer,
&QTimer::timeout,
[&]()
{
m_app->processEvents();
}
);
// Start the timer with the fixed 'message' interval
m_timer->start(kMsgInterval);
}
}
If my DLL should now display a modal dialog, it works (partially) with the following code:
{...}
case eUserIGeneral:
{
qDebug() << "<< eUserIGeneral";
QDialog w;
w.setModal(true);
w.exec();
// --> Code here is executed AFTER the dialog has been closed
}
break;
//-------------------------------------------------------------------
{...}
The code after w.exec() will actually be executed AFTER the dialog was closed (as intended). However, the main application still remains responsive and is not affected by the modality of my dialog, which Is not as I expected it to behave.
How can I make sure that inputs in the main application are locked when calling a modal DLL dialog?
Although I don't have a real answer to your question, there too much stuff wrong with your code to be properly explained in a comment. Therefore I am writing this as an answer.
QApplication::exec(): I strongly recommend revising the decision against it. If you want the window to be modal, why would it be wrong to "block the entire application" until it is closed? Note that you will not block the Qt part of the application, only the one that calls exec.
QTimer: A timer can only run inside an event loop. So the m_app->processEvents() either never executes, or you already have an event loop running. Either way, there is no use for the timer.
w.setModal(): If what this does is not correct for you, check out setWindowModality().
w.exec(): Ignores the value of setModal(). Read the documentation of setModal() and exec() to find out more.
w.exec(): Executes an event loop. If this somewhat does what you want, QApplication::exec() should work too. Just make sure to exit the main event loop when done.
w.exec(): Is not executed after the dialog was closed. It is executes while the dialog is shown. It blocks until the dialog is closed. So you will start executing it, show the dialog, close the dialog, and then return from it. Read the documentation of exec() to find out more.

QWidget will not close in full screen mode on OS X (Yosemite)

I have a child class of QWidget, and I'm trying to fix a bug where the the window that it is in cannot be programmatically hidden/closed using the QWidget::hide() or close() methods.
Here are some of the things that I tried:
if(widget->isFullScreen())
{
widget->showNormal(); //Makes the window normal-sized before closing it
widget->hide();
}
Here's another way I have tried:
if(widget->isFullScreen())
{
widget->setWindowState(Qt::WindowMinimized);
widget->hide();
}
I also tried setting up a slot/signal system:
if(netcam->isFullScreen())
{
connect(this, SIGNAL(fullScreenExited()),
this, SLOT(onFullScreenExited()));
widget->showNormal();
this->fullScreenExited(); //just hides the widget (or closes it)
}
else
{
widget->hide();
}
The result every time is that the window freezes and must be closed by hand. My suspicion is that the first showNormal() is happening asynchronously, and the second close()/hide() never successfully executes.
I also tried this, in hopes that it would complete showNormal() before going on to hide()/close():
if(widget->isFullScreen())
{
widget->showNormal();
QApplication::processEvents();
widget->hide();
}
THE MAIN QUESTION:
Does anybody have any suggestions for how to deal with closing a full screen QWidget from Qt code?
Question that could also help:
Is there a way to ensure that things run synchronously?
Thanks!
EDIT:
The only way that I got this to work was to call showNormal() further up in the process, which prevents overlap in the execution of showNormal() and hide(). I'll try to remember to come back later and give a good, basic example with a regular QWidget.
I should also add that the window is put into the fullscreen state with the + (full screen) button, which is located at the top of each window in OS X.
This is a known bug.
The workarounds of showNormal() or showMinimized() are not working because the window state change is not synchronous. And a single processEvent() is not enough. You need to wait for the corresponding QEvent::WindowStateChange event to know when the window has fully moved out of fullscreen and can receive a new window state change.

How to call a slot on quit

I want to update my database just before my Qt application closes.
I want something like connect(this, SIGNAL(quit()), this, SLOT(updateDatabase()))
One way could be to introduce a quit button, but is it possible to achieve this functionality if user presses Alt+F4?
Use signal aboutToQuit() instead.
This signal is emitted when the application is about to quit the main
event loop, e.g. when the event loop level drops to zero. This may
happen either after a call to quit() from inside the application or
when the users shuts down the entire desktop session.
The signal is particularly useful if your application has to do some
last-second cleanup. Note that no user interaction is possible in this
state.
For example :
connect(this, SIGNAL(aboutToQuit()), this, SLOT(updateDatabase()));
There is another way to do it, not aboutToQuit() signal, but to re-implement the closeEvent(QCloseEvent *event). You can call you slot before the statement event->accept();
like this:
void MainWindow::closeEvent(QCloseEvent *event)
{
call_your_slot_here();
// accept close event
event->accept();
}

Changing QLineEdit TextBox's inside of loop dynamically

I'm building some code where I'm running a while loop and, within the loop, am trying to change the contents of a couple of textboxes with QLineEdit's setText(). However, merely calling setText within the loop does not work; the textboxes only change their actual value once the code has run through, instead of at each iteration.
I have little experience with C++ or Qt, but the project I'm working on must use them. Any help?
EDIT: I'm guessing this must be something simple that I simply am having troubles because of my lack of familiarity/knowledge, but if more information is needed I'll gladly provide it!
The problem is that QT needs control to return to the UI thread's event loop in order to update the QLineEdit's visual appearance. The quick and dirty way to run the event loop is to add QCoreApplication::processEvents() after each call to setText(). The proper way to fix it is to move the blocking process that sets the value of the text box into another thread, expose an updateText(QString text) signal, connect it to the TextBox's setText(const QString & text) slot and emit that signal whenever you want the text to be updated.
Please see my answer to a similar question for more detail: unexplained delay after QProgressBar finish loading
You might also want to check out some of the documentation on QThreads and the Qt signal slot system: http://harmattan-dev.nokia.com/docs/library/html/qt4/threads-qobject.html
In my case, calling only repaint() or processEvents() won't do the job.
Within your function loop, call both QCoreApplication::processEvents(); and repaint();:
for (i;...)
{
//do your calculations
//...
QCoreApplication::processEvents();
repaint();
}
Calling ui->mywidget->update() didn't make any different as well.
(Tested for Qt4.8.3 on Kubuntu 12.10 and Qt5.0.1 on Windows XP)

How to return from exec method programmatically in static singleton class

I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.