Interrupt a function in Qt c++ when it has a timer - c++

I'm trying to make a program in Qt. I will not send all, but the fragment with which I have a problem. The point is, I have to interrupt a function that has a Timer in it, but I don't know how to interrupt it while it's waiting before timer. 😕 Please reply!
void MainWindow::Test(){
TestWrite(); //TestWrite is a function where I write my answer
QTimer::singleShot(5000, this, &MainWindow::TestCheck); //TestCheck is a function where the answer is checked
}
void MainWindow::on_Test_clicked()
{
Test();
timer = new QTimer(this); //Creates a timer and calls the Test function every 7 seconds
timer->connect(timer, &QTimer::timeout, this, &MainWindow::Test);
timer->start(7000);
}
........
........
void MainWindow::on_Back_clicked()
{
timer->stop(); //I am trying to make the "Back" button interrupt the Test, TestWrite and TestCheck functions. Now, if you quickly click "Back" and "Test", the effect is as if you did not click the "Back" button
}

Timers in Qt that you start with the static QTimer functions (like you do in MainWindow::Test) cannot be stopped at all, because there's no timer object exposed to you that you could use to stop() (or otherwise manipulate) the timer. Such timers will expire in any case, and they will emit their signal in any case. You need to decide in your slot whether you're still interested in the signal.
When you create an explicit timer object (as you do in on_test_clicked(), you can only stop() it, or re-start() it with a minimum expiry time (0) to have it expire "immediately".
If you don't want to have a timer expire that you already started, simply stop() it.
The way you stated your question shows a possible mis-understanding of how timers work in Qt. Nothing is "waiting" for a timer, but rather an expired timer sends a signal to a slot. "Waiting" in Qt would freeze your user interface.

Related

QTimer dont stop when windows is closed

I am currently starting on QTCreator. I have been asked to use QTimers in a particular context which is this:
We have an open window,
One or more QTimers are triggered and make things appear on the screen every x msec.
When we press "Escape" the window should close and everything should be reset to 0.
But here is the problem, the timers are defined in a static way:
QTimer::singleShot(500, this, SLOT(foo());
When I call this->close() (which closes my window), the timers do not stop and continue. I tried several solutions: browse all the QTimers contained in my object, obviously there are none since they are defined in static. Instead of declaring them in static I've tried to create each time a new QTimer object like that:
QTimer *timer= new QTimer(this);
timer->setSingleShot(true);
timer->setInterval(2000);
timer->setParent(this);
timer->start();
And then call timer->stop() later, but I think it's very brutal when you have multiple Timers in the same code.
Is there a way to stop the timers when this->close is called, knowing that the timers are defined as a static one ?
Assuming you are using,
QWindow *qw = new QWindow();
QTimer *timer= new QTimer();
To solve the issue you need to connect destroyed() signal of QWindow to timer's slot stop()
So as soon as window is destroyed all registered timers will be stopped without explicit stop call. make sure you connect all timer instances. Code snippet as following,
QObject::connect(&qw, SIGNAL(destroyed()), timer, SLOT(stop()))
QObject::connect(&qw, SIGNAL(destroyed()), timer2, SLOT(stop()))
QObject::connect(&qw, SIGNAL(destroyed()), timer3, SLOT(stop()))
PS:
QTimer *timer= new QTimer(this); // here you are setting parent as 'this' already
timer->setSingleShot(true);
timer->setInterval(2000);
timer->setParent(this); // remove this, no need to set parent again.
timer->start();

Will UI block when waiting for QThread / How to use QThread properly

If I have a progressbar in the ui thread (window), which shall run endless until a method finishes it's work, will the ui thread block and so the progress bar if I'm waiting for a second QThread to finish? If the ui thread blocks waiting, then i would not to wait for the second thread. I thought to implement a callback method which will be called when the second thread finished, but then: How can i connect to the callback method?
What do I want to do?
I have a window, this window has a progressbar which is first not visible. When the user presses a certain button to request data, a method will be called which returns a RequestPointer which contains a method which returns the request status.
When the user presses the button, i want to make the progress bar visible, running infinitely till the request is finished and i can print the data to the window.
To the Worker i want to pass this pointer and the worker checks in a while (flag) loop, if the status is still running and sleep if so. When the worker finishes, i want to stop the progressbar and make it unvisible again. Do i have to pass the progress bar to the thread, or can i wait for the thread without blocking the ui?
I'm not quite a Qt pro. Really new to it. I tried to get some info from the https://doc.qt.io/Qt-5/qthread.html website, but it's a bit hard for me to understand the code example.
Method in my worker class:
void Worker::watchRequest(RequestPtr r_ptr)
{
bool exit = true;
while (!exit)
{
ErrorCode errorCode = r_ptr->Test();
switch (errorCode)
{
case Request_RUNNING:
QThread::msleep(10);
break;
case Request_ABORTED:
exit = true;
break;
case Request_SUCCESS:
exit = true;
break;
}
}
QThread has a finished signal. Connect this one to some appropriate slot, which will trigger any action necessary on thread completion.
I suppose best candidate to know how far the progress went is the worker thread itself. You might create your own signal that sends the current progress to some slot that will do the update of the progress bar appropriately.
Alternatively, you might use a QTimer to read the current progress from time to time (this resembles closer to what you have now, but does not block the UI).
If you don't want to block the user interface, all you have to do is to call QApplication::processEvents(); in your while-loop.
I have some code which uses std::future instead of QThread and my code looks like this:
while (!progressIndicator->UserBreak()
&& (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready))
{
QApplication::processEvents();
}
This works well.
To update the UI Thread's progress bar while the operation is running, use a QTimer object to increment the progress bar's value (Max value will be :One less than Progress bar's value when the operation completes). Also connect the QThread to a slot by Signal/Slot method to signal the UI thread when the operation has ended. When the QThread finishes the operation, send a signal to a Slot in the UI Thread, which will set the final value of the progress bar and also stop the QTimer.

How can I stop a long for loop when the widget (QDialog) running it is closed without multithreading?

I have a quite lengthy foreach loop in a QDialog. It basically looks like this:
foreach (xxx, xxx) {
... doSomeStuff ...
QApplication::processEvents();
if (m_cancelMapLoading) {
break;
}
}
m_cancelMapLoading is set to true by clicking a "Cancel" button. The QApplication::processEvents(); makes this possible.
This works quite fine, but if the dialog is closed as long as that foreach loop still runs, it continues running. I tried to set m_cancelMapLoading to true in each function closing the dialog, but this does not help.
I also tried to test not only for m_cancelMapLoading being true, but also for isVisible(). This actually stops the dialog, but it re-opens it at once without the GUI elements in it.
Unfortunately, QtConcurrent::run etc. can't be used for the function, because the data structures that are manipulated by the foreach loop are not thread safe.
Is there a convenient way to solve this?
You can use a QTimer and Qt's parent-child structure to your advantage here. QTimer with a timeout value of zero has a special meaning in Qt
As a special case, a QTimer with a timeout of 0 will time out as soon
as all the events in the window system's event queue have been
processed. This can be used to do heavy work while providing a snappy
user interface:
So you could do something like
void Dialog::beginDoingStuff()
{
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(processData());
m_timer->start(0);
}
void Dialog::processData()
{
// Perform one cycle of your process here
}
This will perform the processData() function in the same thread as the rest of the dialog, and when the dialog is destroyed by being closed, the timer will be deleted (because it's parent is the dialog), meaning the processing will stop.
A good and quite easy way to unload your GUI from heavy processing is assigning it to another thread or QtConcurrent.
You could then either poll a "should-I-terminate-yet?" variable or terminate the thread manually when it is no longer needed.
I highly recommend a parallel processing since offers better control rather than doing a "DoEvents"-like queue emptying.
We actually managed to solve the problem by connecting the dialog's finished signal to the click slot of the cancel button. This actually stops the loop in all circumstances.
We also introduced starting the function by a QTimer (for a nicer implementation not blocking the function where it's started), but this does not stop the loop (perhaps because we don't destroy the dialog when it's closed).
Thanks for all help :-)

Updating GUI form elements continuously so that Form does not hang

As the title states I am attempting to update a GUI form element continuously via a thread however the form still seems to be busy. This I cannot understand.Here is how I am approaching it.
connect(this,SIGNAL(SIGUpdateForm),this,SLOT(MyUpdateMehtod));
now whenever the form needs to be updated I simply do the following.I launch a method in a new thread . The new thread then triggers the above signal.
boost::thread t(&SomeClass::SomeMethod(),this);
Now once someMethod is started here is what I do
void SomeMethod()
{
SIGUpdateForm(); //Launch the signal that will update the form
}
The SIGUpdateForm then calls MyUpdateMehtod() however since signal (whether queued or direct do not launch any thread thus it seems like the form is hung.) But this confuses me because the signal itself is being called from an independent thread so why is the form hanging ? What can i do to make this work ?
Qt has its own thread. Actually you don't need a thread. QTimer will do it for you. Here is an example.
void updateForm()
{
ui->bla->setText("bla");
// bla bla method
}
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(updateForm()));
timer.start(3000);
Now updateForm() will be called every 3s. GUI will not hang. Another way of doing so is processing the event loop,
while(....)
{
// some lengthy task
qApp->processEvents(QEventLoop::AllEvents);
}

pause functionality in Qt using QTimer on button press

I am trying to implement pause button for a game I am developing in Qt + OpenGL.
I want to implement it using QTimer.
Basically I am updating screen per 100ms. So in order to pause game, I will stop the timer on button press. and when button is again pressed i will start the timer again
Here is my pauseOrPlay SLOT:
void Window::pauseOrPlay()
{
GLWidget::modifyTimer = TRUE;
GLWidget::isPaused = !GLWidget::isPaused;
GLWidget timerUpdater;
timerUpdater.timerFunc();
}
and Here is my timerFunc()
GLvoid GLWidget::timerFunc()
{
static QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
if( GLWidget::isPaused)
timer->start(100);
else
timer->stop();
}
But I am not getting the functionality. I get paused screen on this particular code and upon trying few tweaks here and there, sometimes I get screen updating very fastly which pointed me to this but I was unable to find cure to my problem somehow
Any help or Pointers?
PS: In this question there is nothing about OpenGL, but I think those are the people who might have dealt with similar think, adding OpenGL tag
That connection is in a bad place. You have it set up to connect each time you pause or resume the game. But if you connect multiple times, your slot will be called multiple tiles for each signal emission.
Make sure you only create the timer and connect to it once. I suggest moving the timer construction and signal connection into your GLWidget's constructor. Store a pointer to the timer as a member variable of your class so you can start and stop it in the class's member functions.