[SOLVED] qt threads worker design with blocking while loop
-
i using qt5 and opencv to create an application that give a user inteface using qt and does the image processing part in opencv.....
so far my design is:
i am displaying a video and some standard controls like buttons and checkboxes using qt main gui thread
for capturing image and processing it, i have created a worker class derived from QObject and moved it to a thread....
the function that is executed in the worker class(Worker::process) has a blocking while loop.....that constantly:
- captures a frame from a video or a camera
- does some processing on it
- converts from cv::Mat to QImage
- emit a signal to the main thread to display the QImage
- also in order to recieve user input i was using emitting signal from the main thread to the worker slots
the problem i faced was that the signal from the main thread never got picked off by the worker because of the event loop blocking while loop.
after much searching i came up with the solution to use Qt::DirectConnection argument while connecting the signal from the main thread to the worker slots. that solved the problem at that time.
now i need to add a qtimer or qbasictimer inside the blocking while loop....and guess what, the timer slot(in the case of qtimer) and the protected timerEvent handler (in the case of the qbasictimer) never get called. my hunch is that again the blocking while loop is the culprit
after much searching and reading on forums i have come to the conclusion that somehow my over all design maybe incorrect.....and as i keep adding more functionality to my application these problems will keep showing up.
I have two options now:
somehow call the threads exec() function inside the blocking while loop. so the question to the gurus out there is: "how do i call the thread::exec() method inside a worker QObject class, i need the reference to the thread running the worker to call exec()" (short term solution)
change the whole implementation.....and here the questions is: "what are my options....." (long term)
please feel free to ask for details in case my wording or english has made the problem unclear in any way.....thanks...
-
Hi,
[quote]the function that is executed in the worker class(Worker::process) has a blocking while loop
...
now i need to add a qtimer or qbasictimer inside the blocking while loop….and guess what, the timer slot(in the case of qtimer) and the protected timerEvent handler (in the case of the qbasictimer) never get called. my hunch is that again the blocking while loop is the culprit[/quote]Your analysis is correct. Infinite loops and event loops don't mix -- you must choose one or the other.
[quote]after much searching i came up with the solution to use Qt::DirectConnection argument while connecting the signal from the main thread to the worker slots. that solved the problem at that time.[/quote]I don't recommend doing this. This makes your slots run in the main thread... not in the thread that the object lives in. QObjects are NOT thread-safe, and should not be accessed from two different threads.
[quote]I have two options now:
somehow call the threads exec() function inside the blocking while loop.[/quote]exec() starts running the event loop, which will then block your while loop. An event loop is an infinite loop too :)
Basically, this approach runs a blocking loop inside a blocking loop, which I'm guessing is not what you want.
[quote]
change the whole implementation…..and here the questions is: “what are my options…..” (long term)[/quote]Don't do your processing in an infinite loop. Instead, put the code in a linear function (a slot). Connect this slot to a short timer. Remove Qt::DirectConnection from your code. Your worker thread can now handle your main processing, your timers, and user input.
Note: If you're running Windows, the timer resolution is limited to about 16ms. If you set the interval to 0, it will run at full speed, but also respond to signals.
-
bq. willi wrote:
Can you post some code? Without some code we are discussing about “the King’s beard” on the other hand, I have a similar problem with OpenGL and threads – your solution could give me some inputs …@
class worker : public QObject
{
public:
void process (something);slots: void input from gui(someparams); void stopworker(void);
signals:
void update_gui(somedatastruct);}@
@ void worker::process(somthing)
{
while (condition is true)
{
dosomework; //i have included a mutexlocker
add_delay_here();
}
}@@void gui::on_pushbutton1_click
{
thread = new QThread();
worker = new Worker;
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(this, SIGNAL(stopworker()), worker, SLOT(stopworker()), Qt::DirectConnection);
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(worker, SIGNAL(update_guii(somedatastruct)), this, SLOT(upate_available(somedatastruct)));
capthread->start();
}@@void gui::on_pushbutton2_click
{
emit stopworker();
}@hope this helps in visualising my design......i am new to the forums so forgive any formatting in the code....and any guidance is welcome...be it pertaining to formatting or the actual problem
-
bq. JKSH
Hi, thanks for the very objective and point wise reply. your suggestion have been very helpfull
a few more questions.....
- someone suggested using
@qApps->processEvents();@
is this different from the event loop ??
- i am also doing some time dependant calculations between frames....
e,g tracking....and based on that creating a velocity chart.....i need consistant frame rates atleast to milliseconds...as u suggested using timers, the accuracy wont be that good....is there any other way.....can i somehow use some other timers that are more accurate using some other libraries.....obviously they will have to support qt multi thread.....for e,g opencv highgui functions do not work in qt secondary threads.....otherwise i would have used
@cv::waitKey(milliseconds)@
regards
- someone suggested using
-
You're welcome :)
[quote author="alee" date="1379155097"]- someone suggested using
@qApps->processEvents();@
is this different from the event loop ??
[/quote]Ah, I forgot about that. Yes, if your loop calls qApp->processEvents() every iteration, then it becomes event loop. But, this method isn't any better than calling exec() to let Qt run its own event loop. In particular, this doesn't solve your timing problem.[quote]2. i am also doing some time dependant calculations between frames....
e,g tracking....and based on that creating a velocity chart.....i need consistant frame rates atleast to milliseconds...as u suggested using timers, the accuracy wont be that good....is there any other way.....can i somehow use some other timers that are more accurate using some other libraries.....obviously they will have to support qt multi thread.....for e,g opencv highgui functions do not work in qt secondary threads.....otherwise i would have used
@cv::waitKey(milliseconds)@
[/quote]I just found out that you can use high-precision precise timers by calling QTimer::setTimerType(Qt::PreciseTimer) before you start the timer.Ultimately, timer accuracy is influenced by your hardware. What are your PC specs? Also, what operating system are you using?
But anyway, if the exact time between times between frames is important, you should use a QElapsedTimer to measure the time between frames and calculate your velocity. No matter how precise your timer is, you cannot assume that every iteration has the same interval. This is especially true if:
- Your operating system is not real-time -- Windows, Mac, and "regular" Linux are not real-time; QNX, vxWorks and Real-Time Linux are real-time.
- Your thread is also doing other things -- You mentioned that your thread needs to receive user input and run other timers. These activities can affect your frame timer.
- someone suggested using
-
You're welcome :) All the best!