QProgressDialog does not take input unless the progress value changes
-
When you block the event loop - how should the ui be redrawn? Use the canceld() signal and don't block the event loop...
-
At some point the mouse events need to be processed. For this the event loop needs to run. It is very likely that you are blocking the event loop. There might be some weird behavior when calling setValue with a different progress that uses some direct connection to update the progress bar and also somehow processes the mouse event.
As a quick test you can call QApplication::processEvents() right before your call to wasCanceled() to see if this solves your problem. If it does you are definitely blocking the event loop. However, don't use this trick as your final solution. Your current loop will take about 100 times longer if you do this. It is not worth to slow down your actual processing by 100x just to have a nice UI.
Instead, then your solution should be to use a separate worker thread. You are not allowed to call any UI functions from the worker thread: no call to wasCanceled and no call to setValue. Instead you need to use signals and slots to connect these function calls.
-
@JanLaloux You’re right! Manually incrementing and resetting the progress value is not ideal. A cleaner approach could be calling QCoreApplication::processEvents() inside your loop to allow UI events (like button clicks) to be processed without artificially changing the progress value. You might also try setAutoReset(false) or setMinimumDuration(0) to see if it improves responsiveness.
-
@andrewmorgan
It would not be ideal to callprocessEvents()
. It would be better to allow the main event loop to run and raise and act on thecanceled()
signal, as previously written. -
Thanks all for the valuable input!
@SimonSchroeder: the test with QApplication::processEvents() is positive, the problem goes away.
BUT: the implementation is as documented for the QProgressDialog Class for the modal operation:
"Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler to use for the programmer. Do the operation in a loop, call setValue() at intervals, and check for cancellation with wasCanceled(). "
So with the functions, not the signals.
And since it is the intention that the user cannot do anything than either wait for the operation to finish, or to cancel it, this seems to me correct for the modal operation? -
@JonB said in QProgressDialog does not take input unless the progress value changes:
It would not be ideal to call processEvents()
The only valid use case for
processEvent()
to update the progress ofQProgressDialog
/QProgressBar
is when using it in combination with tasks that run inmain.cpp
before the event loop is even started or like everythingQSplashScreen
related... -
@JanLaloux said in QProgressDialog does not take input unless the progress value changes:
Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler to use for the programmer. Do the operation in a loop, call setValue() at intervals, and check for cancellation with wasCanceled(). "
So with the functions, not the signals.We should adjust the documentation... blocking the event loop is a bad idea.
-
Usually you'll see a pulsing inside the progress bar. If you depend on calling setValue this will not be smooth. It'll always look like your application is hanging. And if you call setValue too often (i.e. often enough for the progress bar to be smooth) it will significantly slow down your actual work. Also, in the former case Windows might report "Not responding" (especially when clicking "Cancel").
We were looking for an easy solution (in our case to just do little changes to our existing code) to fix this problem. What we came up with you can find here: https://github.com/SimonSchroeder/QtThreadHelper. It's a simple header-only library where you can easily create a worker thread using just a lambda and a helper function guiThread to just call setValue inside the GUI thread right from your worker thread. You should still connect the canceled signal to react to this.