Use QCoreApplication::processEvents() in another thread
-
Hi, I have a function that blocks my event loop pretty badly, so I used processEvents() to unblock it; however, as the function description warns this function can take some time and if repeatedly called will slow down your program. Which is exactly what happened to me. I have a camera acquiring images, and I would like a QLabel on my App to update with every picture hence the use of processEvents() so my call to update() on the label gets processed. I was wondering if it's possible to run this function in another thread? I'm pretty sure that by opening a new thread I won't be able to access the event loop anymore, right now I have :
and then a call to
. This runs fine; however, it doesn't unblock the event loop. So is there some way to pass those events into this other thread so they can be executed??
Thanks
-
Hi, I have a function that blocks my event loop pretty badly, so I used processEvents() to unblock it; however, as the function description warns this function can take some time and if repeatedly called will slow down your program. Which is exactly what happened to me. I have a camera acquiring images, and I would like a QLabel on my App to update with every picture hence the use of processEvents() so my call to update() on the label gets processed. I was wondering if it's possible to run this function in another thread? I'm pretty sure that by opening a new thread I won't be able to access the event loop anymore, right now I have :
and then a call to
. This runs fine; however, it doesn't unblock the event loop. So is there some way to pass those events into this other thread so they can be executed??
Thanks
@ejm1 said in Use QCoreApplication::processEvents() in another thread:
I was wondering if it's possible to run this function in another thread?
See https://doc.qt.io/qt-5/qcoreapplication.html#processEvents
It is possible to run the function in another thread because the function is thread-safe. However, it will only process that thread's events. It won't process your GUI thread's events.
Also, a thread started by QtConcurrent does not have an event loop so it doesn't have any events.
So is there some way to pass those events into this other thread so they can be executed??
No. Events cannot be transferred across threads.
I have a camera acquiring images, and I would like a QLabel on my App to update with every picture hence the use of processEvents() so my call to update() on the label gets processed.
What is the frame rate of your camera acquisition?
Painting an image on QLabel uses CPU rendering which is inefficient. Could you use
QCamera
+QVideoWidget
instead?I have a function that blocks my event loop pretty badly
What does that function do? Can you break it up into smaller functions?
-
@JKSH My frame rate without updating the image is around 63 and with the processEvents it drops to about 40. This function that blocks my event loop is the acquisition loop of the camera. It's a while true loop that doesn't stop acquiring images until a button is pressed on the GUI. Without the processEvents this loop would not know that the button was pressed. I can't really break this up as everything must be in the loop.
As for your other idea I have never heard of those classes to be honest. I'll have to look into them. They can't be placed with the QtDesigner right? I'd have to code them in the constructor?
-
Hi,
Can you explain how the image acquisition is done ?
-
@SGaist there is an image buffer that can fit 10 images. If the buffer is full it overwrites one of them depending on the setting I used. Then I pull an image out of the buffer and do some stuff to it such as sending it to my qt application (by reference) and displaying it as a pixmap (this by itself already drops the frames a little but I expected that). Then on my call to processEvents() I lose even more frames because I have to call it for every single frame. But basically I get the images one by one.
-
What is the format of these images ?
-
Depending on the chosen format (I'm working with polarized light) the image can be grey scale intensity values and in that case its an unsigned char* to a long array of pixel values (Mono8). So there's only one color channel. Basically I just pass this unsigned char* as the "Data" for my QPixMap::fromData().
Does this relate to the processEvents calls??
-
Trying to determine what is your bottleneck in order to improve your throughput.
How are you accessing that camera ?
-
@JKSH My frame rate without updating the image is around 63 and with the processEvents it drops to about 40. This function that blocks my event loop is the acquisition loop of the camera. It's a while true loop that doesn't stop acquiring images until a button is pressed on the GUI. Without the processEvents this loop would not know that the button was pressed. I can't really break this up as everything must be in the loop.
As for your other idea I have never heard of those classes to be honest. I'll have to look into them. They can't be placed with the QtDesigner right? I'd have to code them in the constructor?
@ejm1 said in Use QCoreApplication::processEvents() in another thread:
I can't really break this up as everything must be in the loop.
My suggestion is that you try to break up the loop. The easiest replacement is a QTimer with a timeout of 0ms.
- Make the body of your loop a separate slot. (The loop variable needs to become a member variable.)
- Call this slot once by hand to get the first iteration of the loop.
- At the end of the slot create a singleshot QTimer which calls the slot when terminated. Don't forget to increment your loop variable if you have one.
A QTimer with a 0ms timeout will fire as soon as possible. It basically places the next iteration of the "loop" into the event queue. Now, you don't have to call
processEvents()
anymore. This could improve the speed of your video output.Another approach to your problem is to put your loop into an extra thread and thus not block the regular event loop. Be careful with GUI calls. These need to be put into the GUI event loop. You can use
QMetaObject::invokeMethod(qApp, ...)
for this (theqApp
in there is to send it to the context of the GUI thread). I prefer lambdas in this scenario:QMetaObject::invokeMethod(qApp, [this](){ m_label->setText("test"); });
-
Hi Simon thanks for the response. I'll have to look into the first option, but I do not think the second option will work for me. If the loop is in another thread then any variables that change outside of that thread won't be updated in the loop and I need the loop to be aware that these variables changed (e.g. the user pressed stop to end the loop). I'm not the best programmer, but I don't think there's a way to make a separate thread aware of these changes.
As for your first idea, you're basically saying just make my one iteration of my loop an event essentially, so that way everything is always updated afterwards. Why would this speed things up as it's basically equivalent to calling processEvents I think???
-
@ejm1 said in Use QCoreApplication::processEvents() in another thread:
If the loop is in another thread then any variables that change outside of that thread won't be updated in the loop and I need the loop to be aware that these variables changed (e.g. the user pressed stop to end the loop).
All variables are by default shared between threads. So, this approach should work for you as well. You can have, e.g., a boolen variable
m_stop_looping
which you set totrue
in the GUI thread and the looping thread will pick it up eventually. 100% proper programming would synchronize access to this variable, e.g. using an atomic variable. However, I would say that you don't need to do this. If you check the variable in every iteration of the loop (and hopefully one iteration does not take too long), you will at most do one extra iteration of the loop. Basically this suggestion is just the inverse of your original post: instead of callingprocessEvents()
in a separate thread (which would handle the GUI stuff if it worked) and doing the work in the current thread, you swap these two threads: Put the work into a separate thread and implicitly have theprocessEvents()
through the regular event loop in the GUI thread.As for your first idea, you're basically saying just make my one iteration of my loop an event essentially, so that way everything is always updated afterwards. Why would this speed things up as it's basically equivalent to calling processEvents I think???
I am not entirely sure why this is faster. This is just my experience in general. Maybe there is some overhead involved in call
processEvents()
compared to just picking the next event from the event loop. I am also not entirely certain if theQTimer
event will be pushed to the top of the event loop. I believe Qt does something smart here which helps with what you want.