How to properly using infinite loop in Qt GUI? - c++

I am new to and learning the Qt GUI framework.
I have an ultrasonic sensor wired up to the Raspberry Pi, to measure water level. If I were coding in C, I would have used a while(1) loop to constantly read the sensor input. But when I put while(1) inside MainWindow.cpp, the window cannot be displayed. However, using qDebug() I can still print out the sensor value, which means my while(1) still running but the main window won't appear. I found out in this answer that because of while(1),
MainWindow ctor never returns, so w.show() is never called and a.exec() (main message loop) is never executed.
To solve this, I use QTimer instead of a loop: connect the timeout() SIGNAL to a SLOT which is a function to read the sensor value one-time:
waterLevelTimer = new QTimer(this);
connect(waterLevelTimer, SIGNAL(timeout()), this, SLOT(getWaterLevel()));
waterLevelTimer->start(100); // "loop" once every 100 millisecond
With this method, I can read the sensor value with the fastest interval is 1 millisecond and the GUI still displayed fine.
But should I use QTimer to mimic a while(1) loop? Is there a better way to have an infinite loop to read GPIOs while still being able to use GUI for other work?

The main thread where the GUI of Qt runs never should be blocked by long-lasting operations like an infinite while-loop, because otherwise you would block the event system and nothing will work anymore.
Instead you usually create a worker thread in parallel (see QThread) where you do your loop in the run function of the thread. Maybe also use such timer as you suggest, which works if the executed code is faster than the timer duration.
QThread *thread = QThread::create([]{
while(1)
checkSomething();
});
thread->start();

Related

Why does qt show () not have priority?

I'm using Qt for an application.
I would like to display a window then put a timer and display a second window.
But currently the timer is done then the 2 windows open at the same time
this->firstWindow->show();
QTime dieTime = QTime::currentTime().addSecs(10);
while (QTime::currentTime() < dieTime);
this->secondWindow->show();
I tried a lot of solutions, like putting the show() of the firstwindow directly into the constructor but nothing works.
You are using a blocking while loop to wait for the time to elapse, so the GUI thread cannot update the user interface. You could use QTimer for a non-blocking wait or refresh the GUI by adding qApp->processEvents(QEventLoop::AllEvents, 100); into the while loop.
I would prefer QTimer, because then you are not creating your own event loop. For example:
QTimer::singleShot(10000, this->secondWindow, SLOT(show()));

Using sleep_for() in Qt application

In my application I want to display some image with this code
myImage = scene->addPixmap(image);
myImage->setOffset(x, y);
Then I want to sleep for some seconds:
std::this_thread::sleep_for(std::chrono::seconds(2));
and then display another image. But this code waits for 2 seconds at first and then displays both images at once. How can I make the program wait between displaying those two images?
Use QTimer::singleShot() and connect it to a slot that updates the picture:
class MyObject : public AQObjectInheritingClass
{
Q_OBJECT
...
public Q_SLOTS:
void changeImage()
{
//change image here
}
And in instead of sleep_for():
QTimer::singleShot(2000, &instanceOfMyObject, SLOT("changeImage()");
Two seconds later, changeImage() will be invoked.
You cannot sleep in event based application (GUI applications are event-based). Event based applications have main loop, that has to be running to process events, and update its state. Calling sleep_for stops that main loop, so your app doesn't process events so it doesn't react to input and doesn't repaint itself. That's why all drawing if deferred fot two seconds then flushed at once.
As an alternative, you should use timers, such as QTimer
there is Qt-specific non-blocking way to pause thread with QEventLoop:
QEventLoop loop;
QTimer::singleShot(1000,&loop,SLOT(quit()));
loop.exec();
While loop is being executed, all other events for this thread are processed.

Multithreading advice

I am developing an application, the main goal is to grab images from a frame grabber, do some processing and then show images on a GUI.
The frame grabber is connected to the PCIe. And I'm using the frame grabber SDK.
The image stream is pretty slow 10 to 100 images/s
I am here to have some advice about my code and how to optimize it.
First, there is my run() function from a class inherited from Qthread
I grab an image and put it on a buffer queuecv:: Mat>.
void ImageIn::run(){
_cam->allocMemory();
_cam->startAquisition();
_runningThread = true;
while(_runningThread)
{
Mat image(_cam.getSizeX(), _cam.getSizeY(), CV_16U, _cam->getImageDMA0());
_ctrl->getMutexIn()->lock(); // Lock BufferIn
_ctrl->getBufferIn()->push(image); // store Image in BufferIn
_ctrl->getMutexIn()->unlock(); // Unlock bufferIn
}
}
Images are stored in a buffer and then the processing thread do some work...
void ImageProcessing::run(){
while(_runningThread){
if (_ctrl->getMutexIn()->tryLock()){
while(!_ctrl->getBufferIn()->empty()){
_ctrl->getBufferIn()->front().convertTo(tempConvert, CV_32F);
_bufferLocalIn.push(tempConvert);
_ctrl->getBufferIn()->pop();
}
_ctrl->getMutexIn()->unlock();
}
// Do some processing and put image and a buffer for GUI
}
}
So, I have some questions:
- The thread 1 gets images thanks to a blocking function, so the CPU consumption is low, but the thread 2 run continuously and consumes a lot of CPU reassess what can I do for fixing that?
- is it the right way to code that?
So i tried that :
QThread* thread = new QThread;
ImageWriter* worker = new ImageWriter();
worker->moveToThread(thread);
QTimer* timer = new QTimer();
int msec = 100;
timer->setInterval(msec);
QObject::connect(thread, SIGNAL(started()), worker, SLOT(process()));
QObject::connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
QObject::connect(timer, SIGNAL(timeout()), thread, SLOT(start()));
timer->start();
is it ok ?
As Qt is an event driven framework, each thread has an event queue which receives events and sends them to various objects to receive them.
If any section of code in a thread has essentially a while(1) loop, then event propagation cannot occur and this is what you're doing in both threads with
while(_runningThread)
Starving the event processing in an infinite loop is the cause of excessive CPU consumption.
It is possible to use to get Qt to process events with a call to QApplication::processEvents within the infinite loops, but this is not ideal.
A better method would be to time-slice the processing with QTimer and allow the event propagation to occur naturally. This would require deciding how long processing could occur, before saving the processing state and then returning back to the event loop. A tick of the timer would call your processing function which can then restore state and resume.
This method will be easier if you create a processing class derived from QObject which you move to a QThread, rather than inheriting directly from QThread itself. There's a great article on How to Really Truly Use QThread which can be used as a template on how to do this.
Finally, consider that you can have more than one QThread, as well as more than one QObject running on a QThread. As a general rule, you will not benefit if the number of threads exceeds the number of processor cores available.
If you know the target machine is a quad core, you can create 3 extra QThreads (4 in total, including main), create multiple processing objects and move them to different threads to provide optimal processing.

Run the code continuously until "Stop" is pressed

I want to execute some code when I press a "Start" button and it have it continue running until I press "Stop".
However, with the following code once I press "Start", I can't press "Stop" anymore:
void MainWindow::on_pushButton_clicked()
{
if (ui->pushButton->text()=="Start") {
ui->pushButton->setText("Stop");
vec spectrum_sensed,Sn,sigma,alpha;
mat ydata;
condition=true;
while (condition) {
cvec uhd_samples = to_cvec(randn(Nsamples),randn(Nsamples));
ydata=spectrum->dft(uhd_samples,Ndft,Nband);
myplot->Plot_data(spectrum_sensed,Ndft);
}
}
else {
ui->pushButton->setText("Start");
condition=false;
myplot->clear_plot();
}
}
Qt's GUI runs in a single thread, and if your code blocks that thread, things like mouse clicks won't ever get handled (because the thread is busy doing nothing but calculating your data), and so your GUI freezes up.
As for how to change your program so that doesn't happen, you have a few options:
You could just throw a call to qApp->processEvents() into your calculation loop. That way Qt will get a chance to handle things like mouse clicks while you are calculating. That's the easiest way to go, but it can bite you if you're not careful -- for example if you've set up some other code so that a mouse click causes your MainWindow to be deleted, then you're likely to crash because after processEvents() returns it find itself executing in the on_pushButton_clicked() method of a now-deleted MainWindow object(!).
You could spawn a separate thread (using the QThread object) and run your calculation loop there. This is probably the most efficient approach, since on a multicore CPU it will let the GUI event loop run on one core while the calculations-loop simultaneously runs on another core, and so neither loop will slow the other one down. Note that this approach only works if your calculation loop doesn't touch any of the Qt GUI stuff -- Qt's GUI widgets are meant to be accessed only by the GUI thread, and if you try to examine them or modify them from a different thread, bad things will happen.
You could "unwrap" your loop so that it's no longer a synchronous loop but rather more like a state machine: That is, move the declarations of your calculation's state variables (spectrum_sensed, Sn, sigma, alpha, and ydata) out of the method, so that they are now member variables of the MainWindow class instead. Then write a Slot method that just does one iteration of your while loop (or maybe a small number of iterations, depending on how long each iteration takes), and then (within a few dozen milliseconds) calls QTimer::singleShot(0, this, SLOT(TheCalculationSlotMethodName()) and returns. That QTimer::singleShot() call will cause your calculation-slot-method to be called again on the next iteration of the Qt event loop, whereupon it can do a little bit more calculation. When the user clicks the "Stop" button, just have your on_stopButton_Pushed() slot set a boolean or something so that your calculation-method will know not to call QTimer::singleShot() anymore. I like this approach because there is little risk of race conditions or re-entrancy problems.

How do I stop and start a loop in QT?

I've been trying to figure out how to make the program stop in a for-loop and wait for a button to be clicked and the for-loop continues.
Isn't there any easy way to stop the loop like QSystem.stop() and in the button clicked function QSystem.star() and the loop continues.
In C++ you could use system("pause") or the program stopped when you used a cin<<. But how do i do this in QT?
Since the userinterface needs its code to run, if you halt event loop (which also means that any function that gets called from within the event loop is blocking, waiting for some event), you also halt the user interface, which means clicks into the window won't be processed. That's why in event based programming, which is what all UI kits do, it is essential to return to the main event handler loop as quickly as possible.
What you can do is create a second thread and wait on a condition variable. The GUI thread can signal that condition variable in the button click event slot.
In C++ you could use system("pause") or the program stopped
Exactly: The program is stopped. Which means it won't fetch events from the operating system. However receiving data from stdin is not an event. It's blocking on streamed I/O. Graphical user interfaces are event based though.
Note that conceptually it's not really impossible to think of a user interface to provide streaming I/O channels. However that doesn't work in single threaded programs. You need parallel execution (coroutines, threads, or such) for a concept like this to work.
You don't need threads, nested event loop will do the job.
int waitUntilSignalIsEmitted(QObject *sender, const char *signal) {
QEventLoop loop;
connect(sender, signal,
&loop, SLOT(quit()));
return loop.exec();
}
// usage:
while(yourLoopCondition) {
// some stuff
...
// pause here
waitUntilSignalIsEmitted(yourContinuationButton, SIGNAL(clicked()));
// loop continuation
...
}