Application/process runs after exiting
-
In my program, I have a single button, which is checkable. When I first press it, it prints "test" every second; when I press it again, the loop which prints stops, and so on.
This is the code:
void MainWindow::on_pushButton_clicked(bool checked){ if(checked){ bloop=true; QTimer timer(this); QEventLoop loop; QObject::connect(&timer, &QTimer::timeout,&loop,&QEventLoop::quit); while(bloop){ qDebug()<<"test"; timer.start(1000); loop.exec(); } } else{ bloop=false; } }
bloop is a bool declared in MainWindow class.
As you can see, the second press on the button makes bloop false, which exists the while(bloop) loop, started previously from the first click on the button. The application works how expected, but if I close the program(from the X icon) while the loop is started, the GUI closes, but I can still see "test" getting printed in the output, and also the process being alive. Also, "test" is printed without the 1000ms delay after closing the program.
How could I prevent this ? -
Wow. This is pretty convoluted way to just run a timer. I'd seriously rethink that and simplify. Anyway...
By default Qt application exits when last window is closed. The implementation detail for this is that it is done by setting a "special flag" on a thread data that makes loops quit and prevents new ones from starting. This is done so that the usual
a.exec()
in themain()
quits.A side effect is that when you close the window your local event loop quits. Since
bloop
was never set back tofalse
the while loop keeps rolling and tries to start another loop. This time theloop.exec()
fails because of the "special flag" set, so it quits immediately (or rather never starts). The while loop keeps on spinning like this indefinitely and, since the loop never starts inside, you see the stream of "test" on the output without delays. The timer is reset every time so it never timeouts anyway if the loop didn't started.The fix is to check the return value of
loop.exec()
. If it fails to start it returns non-zero value, so you can check it to know you should quit your while loop:... if (loop.exec() != 0) break; ...
-
Thanks Chris, that did the trick.
Do you know a better way of running a timer, and keeping everything in one function ?Just wondering, is there no way I could control the exit(X) event, and set bloop to false when exit(X) is pressed ?
I've tried connecting a slot which makes bloop false to the QoreApplication::aboutToQuit() signal, but with no success. -
- make
QTimer timer;
a member ofMainWindow
- in
MainWindow
constructor call:
timer.setInterval(100); connect(&timer, &QTimer::timeout,[](){qDebug()<<"test";});
- use
on_pushButton_clicked()
to start or stop the timer:
void MainWindow::on_pushButton_clicked(bool checked){ if(checked) timer.start(); else timer.stop(); }
- make
-
@VRonin
That's a shorther verison indeed. However, the program I provided above only contains the part which was causing the process to keep running, from a bigger project.This is the complete pushButton_clicked function:
void MainWindow::on_pushButton_clicked(bool arg) { if(arg){ flow=true; keeploop=true; ui->pushButton->setText("Stop"); fps=ui->spinBox->value(); pse=ui->spinBox_2->value(); QTimer timer(this); QEventLoop loop; QObject::connect(&timer, &QTimer::timeout,&loop,&QEventLoop::quit); while(keeploop){ for(it=pics.begin();it<pics.end()&& flow;++it){ ui->label->setPixmap(*it); timer.start(fps); loop.exec(); } if(!ui->checkBox->isChecked()){ keeploop=false; ui->pushButton->setText("Start animation"); ui->pushButton->setChecked(0); } else{ timer.start(pse); loop.exec(); } } } else{ flow=false; keeploop=false; ui->pushButton->setText("Start animation"); } }
It displays pixmaps from vector pics, with "fps" delay between the pics, and "pse" delay if the checkbox(for looping) is enabled.
I need to figure out where to put the if (loop.exec() != 0) break; because I have a nested loop.*Later edit: Something strange: In my complete program, I've replaced both lines of loop.exec(); with qDebug()<<loop.exec(); and after closing the program it prints "0" unlike in the first example I provided.
-
I managed to solve the problem for the second program too.
Firstly, by overriding this function:void MainWindow::closeEvent(QCloseEvent *event){ flow=false; keeploop=false; event->accept(); }
And secondly , by adding
if(!this->isVisible()){ QApplication::quit(); }
at the end of the clicked function. Without this if, the infinite loops ends, but the program remains somehow alive,
-
You don't need an event loop at all, QTimer is already a loop. the idea is to use the slot associated with
QTimer::timeout
to change images and the slot associated with the button to change the QTimer parameters + start/stop it.I don't understand what the loop checkbox does however so I cannot put my code here