I just wrote some QThread based code that executes a big calculation. To visualize the progress I need to open a QProgressDialog. The dialog is application modal (using open()) since I do not want to allow modifications of the main window during calculation. The thread emits various signals that allow state machine based communication between GUI and thread.
Two of the signals emitted by the thread's worker object are "Progress" and "Finished". If "Progress" is emitted I am updating the QProgressDialog using setValue(). If "Finished" is emitted the dialog is destroyed.
The following happens at the end of calculation:
- "Progress" event (100%) is emitted
- "Finished" is emitted directly after
- setValue(100) is called due to "Progress" event
- Because the dialog is modal, setValue() calls processEvents()
- processEvents() delivers the "Finished" event
- The "Finished" event causes the Dialog to be destroyed in the middle of setValue() which causes a crash
QProgressDialog breaks my architecture by calling processEvents() in setValue(). Also my coding conventions forbid usage of any nested event loops (like in exec() etc.).
I have two questions:
Why does a modal dialog require a nested event loop? From my undestanding blocking the parent windows' input seem not to require this.
Is it possible to use QProgressDialog in a modal way but without a nested event loop?
you should use
deleteLater()to destroy yourQProgressDialog. The event that deletes yourQProgressDialogobject is handled within a function that belongs to theQProgressDialogobject itself, this boils down to the legitimacy of callingdelete this;within a c++ member function, you can refer to this question from the isocpp C++ FAQ for more information about this. The gist of it is that you should guarantee that you no longer access any member of the object after committing suicide...since you cannot guarantee this in the Qt's
QProgressDialog::setValue()implementation, an event thatdeletes theQProgressBarlike that will happily invoke UB on the next access to any member of the object (when picked up within a member function).deleteLaterwas specifically designed to solve this kind of issue as deferred delete events are handled in a special manner (they are not picked up byQCoreApplication::processEvents()). This means that theQProgressDialogobject will be destroyed aftersetValuereturns control into the event loop and not in the middle of executingsetValue...Always use
deleteLaterin situations like this. When using plaindeletewithin an event, you have to make sure that this event doesn't get handled while executing a member function of this object, and that it doesn't get executed as a result of emitting a signal from this object (with a direct signal/slot connection) since, after all, a signal is just a member function whose implementation is provided by Qt's MOC)...