How to start a loop on a QThread - c++

How can I start a loop on a different thread by pressing a QPushButton?
The main idea is, when pushButtonStart is clicked, start a loop on a QThread, and when pushButtonStop is clicked, stops the loop in the QThread.
The loop can be done by QTimer, for loop or while loop, but he needs a way to stop by pressing a button.

I have this code to create a new timer and set up a connection when it fires. This is in the Start code.
checkTimer = new QTimer(this);
connect( checkTimer, SIGNAL(timeout()), this, SLOT(checkTimerFired()) );
checkTimer->start(3000);
My "stop running" button sets programCreated to false, and checkTimerFired starts with this:
if (!programCreated) {
checkTimer->stop();
return;
}
That should be the Qt-specific things you need. The rest is simple C++.

To do the loop, I use QTimer, which run the slot pressButton multiple times until stop(); function of QTimer is executed. Of course, to use this method I have to recode the program, to improve the algorithm.
QTimer loop:
void MainPrograma::on_pushTurnOn_clicked()
{
tierLoop = new QTimer(this);
timerLoop->setInterval(5000);
timerLoop->setSingleShot(true);
connect(timerLoop, SIGNAL(timeout()), SLOT(on_pushTurnOn_clicked()));
QPushButton *ThirdButton = uimain->QPushButton_3;
Nmb2 = 2
Nmb4 = 4;
if(Busy==1){
ThirdButton->setEnabled(false);
}
if(Busy==0){
timerLoop->start(); //start the loop
StartFunction(Nmb2, Nmb4);
if(X==1){
ThirdButton->setEnabled(false);
}
if(X==0){
ThirdButton->setEnabled(true);
}
}
}
void MainPrograma::on_pushTurnOff_clicked()
{
timerLoop->stop(); //stop the loop
}
QTimer declaration on .h:
private:
QTimer *timerLoop;
I still do not understand how to use QThread... But the loop is already answered!

Related

Change interval timeouts of QTimer with slider

timer = new QTimer(this);
timer->setInterval(50);
QPushButton *start = new QPushButton("Start/Stop", this);
start->setText("Start/Stop");
layout->addWidget(start);
connect(start, &QPushButton::clicked, this, [this]() {
if (!timer->isActive()) {
timer->start();
} else {
timer->stop();
}
});
connect(timer, &QTimer::timeout, this, [&slider, sliderDisplay]() {
//increment slider value until at max, then reset to min
}
How can I use another slider to change the interval of timeouts emitted by my QTimer? I've tried using setInterval but it seems that I can't change the interval once it is set. Is the best way to do this just delete this QTimer and create a new QTimer with the specified interval every time?
OP has claimed that
How can I use another slider to change the interval of timeouts emitted by my QTimer? I've tried using setInterval but it seems that I can't change the interval once it is set.
I wondered a bit about that claim because I could've sworn that you can change the interval at any time.
The only restriction, I would consider: the new set interval might not be considered before the next timeout happens. (Usually, I use single-shot timers or timers with small intervals so that you wouldn't notice the difference.)
While playing with my demo, I got the impression that a call of QTimer::setInterval() restarts the interval.
Unfortunately, the doc. of QTimer::setInterval() doesn't mention this behavior explicitly, except:
Setting the interval of an active timer changes its timerId().
Thanks to #Scopchanov who had the look into source code (I was too lazy to).
QTimer::setInterval():
void QTimer::setInterval(int msec)
{
inter = msec;
if (id != INV_TIMER) { // create new timer
QObject::killTimer(id); // restart timer
id = QObject::startTimer(msec, Qt::TimerType(type));
}
}
So, in fact, setting the interval while the timer is running, it kills the currently running timer and restarts a new one.
My MCVE for demonstration – testQTimer.cc:
// Qt header:
#include <QtWidgets>
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Test QTimer");
QFormLayout qForm;
QSlider qSliderInterval(Qt::Horizontal);
qSliderInterval.setRange(100, 1000);
qForm.addRow("Interval: ", &qSliderInterval);
QSlider qSliderStep(Qt::Horizontal);
qSliderStep.setRange(1, 10);
qForm.addRow("Step: ", &qSliderStep);
QPushButton qBtnStartStop("Start / Stop");
qForm.addRow("Timer:", &qBtnStartStop);
QSlider qSliderAnim(Qt::Horizontal);
qForm.addRow("Animation:", &qSliderAnim);
qSliderAnim.setRange(0, 100);
qWinMain.setLayout(&qForm);
qWinMain.show();
// setup timer
QTimer qTimer;
qTimer.setInterval(qSliderInterval.value());
// install signal handlers
QObject::connect(&qSliderInterval, &QSlider::valueChanged,
[&](int value) { qTimer.setInterval(value); });
QObject::connect(&qBtnStartStop, &QPushButton::clicked,
[&]() {
if (qTimer.isActive()) qTimer.stop();
else qTimer.start();
});
QObject::connect(&qTimer, &QTimer::timeout,
[&]() {
int value = qSliderAnim.value() + qSliderStep.value();
if (value > qSliderAnim.maximum()) {
value -= qSliderAnim.maximum() - qSliderAnim.minimum();
}
qSliderAnim.setValue(value);
});
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.13.0

How can I pause an action in real time (Qt5)?

I have a function that reads some commands and does things related to that commands. The problem is that I want to pause the program after each command. This is my code:
void MainWindow::moveDown(){
QPoint l = ui->label->pos();
int x = l.rx();
int y = l.ry();
if(y+50 <= 630){
QPixmap pix(":/resources/img/Penguin.png");
int w = ui->label->width();
int h = ui->label->height();
ui->label->setPixmap(pix.scaled(w,h,Qt::KeepAspectRatio));
y = y+50;
ui->label->setGeometry(x, y, 50, 50);
//sleep(1);
}
}
As you can see, I have tried the sleep() function, but it pauses the program before the label starts moving. What else should I try?
I implemented this function many years ago that I've been using since. It keeps the event loop going so that the UI doesn't freeze during the sleep. It does this by executing a local event loop for the requested amount of milliseconds. It also aborts the loop if the application wants to quit:
/*
* Sleep for 'ms' milliseconds without freezing the UI.
*/
void sleepLoop(const long ms)
{
QEventLoop idle_loop;
QTimer timer;
timer.setSingleShot(true);
QObject::connect(&timer, &QTimer::timeout, &idle_loop, &QEventLoop::quit);
QObject::connect(qApp, &QCoreApplication::aboutToQuit, &idle_loop, &QEventLoop::quit);
timer.start(ms);
idle_loop.exec();
}

Qt, C++, How to Quit QThread

I have a calculator and a calculator method startCalculations() which is to put onto a QThread. I successfully connect mStopCalcButton and the thread's quit()/terminate(). However, when I press mStopCalcButton, the thread does not quit/terminate.
Here is the code in question...
mStopCalcButton->setEnabled(true);
QThread* thread = new QThread;
Calculator* calculator = new Calculator();
calculator->moveToThread(thread);
connect(thread, SIGNAL(started()), calculator, SLOT(startCalculations())); //when thread starts, call startCalcuations
connect(calculator, SIGNAL(finished()), thread, SLOT(quit()));
connect(calculator, SIGNAL(finished()), calculator, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
connect(mStopCalcButton, SIGNAL(released()), thread, SLOT(quit()) );
In the calculator, this is the only defined method...
void Calculator::startCalcuations()
{
int x = 0;
while (true)
qDebug() << x++;
}
Why does my QThread not quit?
The first thing, function QThread::quit() only tell that thread to exit it's event loop, but do nothing related to terminate or exit. you can read Qt document here: QThread:quit()
To terminate a thread, in general implement, you should change your thread's running function code by using stop flag rather than infinitive loop. Whenever you want to terminate thread, you only need change that stop flag and wait for thread terminating.
Using stop flag:
void Calculator::startCalcuations()
{
int x = 0;
while (!mStopFlag) {
qDebug() << x++;
// In addition, you should add a little sleep here to avoid CPU overhelming
// like as msleep(100);
}
}
Terminate thread by turning on the stop flag:
void YourClass::requestTerminateThread()
{
mStopFlag = true;
if(!thread.wait(500))
{
thread.terminate(); // tell OS to terminate thread
thread.wait(); // because thread may not be immediately terminated by OS policies
}
}
In addition, as you can see my comment on above code, you should add some thread sleep time to avoid CPU overhelming.
For more information, please clearly read QThread document specs first.

Insert text in while(1) loop to texteditor in QT

Am trying to print "Some text" to QTextBrowser, continuously for "n" time. Where "n" is integer. For this I have used QTimer::SingleShot for timing. Once the timeout is triggered a FLAG is set to false and this "FLAG" is monitored in while loop to break when FLAG is false and it shall insert the text till FLAG is set to FALSE. Initial value for FLAG is true.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
FLAG = true;
}
void MainWindow::on_pushButton_clicked()
{
ui->pushButton->setEnabled(false);
RunTheTimer();
int counter = 0;
do
{
ui->textBrowser->insertPlainText(QString("Inside While loop %1 \n").arg(counter++));
counter++;
}while(FLAG);
FLAG = true;
}
void MainWindow::RunTheTimer()
{
ui->textBrowser-> insertPlainText("Timer Started");
QTimer::singleShot(60000, this, SLOT(Update()));// One Minute
}
void MainWindow::Update()
{
ui->textBrowser-> insertPlainText("Timeout signal triggered");
ui->pushButton->setEnabled(true);
FLAG = false;
}
MainWindow::~MainWindow()
{
delete ui;
}
Application is getting Hang, When I click Pushbutton.
After debugging I observed, timeout is not triggering once the execution is entered to while(1) loop and application is not able to insert any text inside while(1) loop. Why this behavior? What am I doing wrong?
Thanks.
You are not returning control to the event loop, many things in Qt are not designed to work without an event loop, Have a look at this page from the Qt wiki, in your case:
QTextBrowser won't be able to show the newly added text, since this requires the widget to be able to receive paint events (and this is impossible without an event loop).
The timer that sets your flag to false won't be able to fire, since your program is always busy executing your while loop, and it won't be able to do anything else (unless it gets out from that while loop and this is impossible if it does not set your flag to false. . .).
Instead of using an endless loop, if you want to execute something as repeatedly as possible, you can use a QTimer and set its interval property to 0, this is a special value that causes the timer to timeout as soon as the event loop finishes processing all events in the event queue.
Using the above approach instead of your endless loop, you can use another timer to stop the above timer after a specific amount of time, and you don't have to worry about events not arriving and timers not firing since the event loop is always executing now.
Here is a possible implementation of the above approach:
#include <QtWidgets>
int main(int argc, char* argv[]){
QApplication a(argc, argv);
//set up GUI
QWidget widget;
QVBoxLayout layout(&widget);
QTextBrowser textBrowser;
QPushButton button("Add Text");
layout.addWidget(&textBrowser);
layout.addWidget(&button);
//timer with 0 interval instead of while loop
QTimer workTimer;
workTimer.setInterval(0);
int counter=0;
QObject::connect(&workTimer, &QTimer::timeout, [&]{
//add text to textBrowser whenever the workTimer fires
textBrowser.append(QStringLiteral("Additional Text %1").arg(counter++));
});
//when the button is clicked
QObject::connect(&button, &QPushButton::clicked, [&]{
//start work timer
workTimer.start();
button.setEnabled(false);
//stop work timer after 5 seconds
QTimer::singleShot(5000, [&]{
workTimer.stop();
button.setEnabled(true);
});
});
widget.show();
return a.exec();
}

Qt change picture on timer

I'm trying to change the image on QLabel in Qt.
At first, I did the basic set image as follows:
void MainWindow::setMainDisplayNew(QString imageName){
QPixmap pix7 = imageName;
QPixmap pix8 = pix7.scaled(QSize(720,480), Qt::KeepAspectRatio);
ui->mainDisplay->setStyleSheet(imageName);
ui->mainDisplay->setPixmap(pix8);
}
Now I want to change this so I can pass 2 arrays. List of images and duration they should appear for and I want the display to show them for the indicated duration.
void MainWindow::setMainDisplay(QString imageName[], int size)
{
for(unsigned int i=0; i<size; i++)
{
QTimer * timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=](){
setMainDisplayNew(imageName[i]);
timer->deleteLater(); // ensure we cleanup the timer
});
timer->start(3000);
}
}
EDIT
With the help of the responses, I reached the above code. I am sending the 3 images. It is display the final image after 3 seconds and stays as is... Any help?
while(true){
This is your problem. Qt is an event-driven framework. The while(true) prevents events from being processed. Such events include the timeout from the QTimer and updating of the GUI. You need to allow the function to exit.
In addition, you're not cleaning up your timers, so you're leaking memory every time you enter the function (although that's currently only once!). You can clean up with a call to deleteLater.
Using C++ 11, it would be something like this: -
for(int i=0; i<mySize; i++)
{
QTimer * timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=](){
setMainDisplayNew(imageName[i]);
timer->deleteLater(); // ensure we cleanup the timer
});
timer->start(duration[i]);
}