'wxPython How to correctly close wxProgressDialog when set to Pulse()
I have recently migrated an application from python2.7.15 (wx 3.0.2.0) to python 3.7.9 (wx 4.1.1)
The application uses a wx.ProgressDialog set to Pulse() while waiting for some tasks to complete and then uses Destroy() to attempt to close the ProgressDialog window.
It runs without errors on Python27 but it crashes on Python37. In both ProgressDialog.Destroy() does not actually seem to close the dialog box but it is closed when the next wx element is displayed.
import wx
from time import sleep
def main():
app = wx.App()
progress_dlg = wx.ProgressDialog("Model Manager", "Connecting...")
progress_dlg.Pulse()
sleep(5) # some long tasks are done here
progress_dlg.Destroy()
print("waiting")
sleep(5)
extracted_names = ["ONE", "TWO", "THREE"]
CANDialog = wx.SingleChoiceDialog(None, "Please select:", "Selection", extracted_names)
CANDialog.SetSelection(0)
if CANDialog.ShowModal() == wx.ID_OK:
choosen_can = CANDialog.GetSelection()
else:
return
print(extracted_names[choosen_can])
if __name__ == '__main__':
main()
In python37 this gives the following error:
if CANDialog.ShowModal() == wx.ID_OK:
wx._core.wxAssertionError: C++ assertion ""wxEventLoopBase::GetActive() == m_tempEventLoop"" failed at ..\..\src\generic\progdlgg.cpp(718) in wxGenericProgressDialog::~wxGenericProgressDialog(): current event loop must not be changed during wxGenericProgressDialog lifetime
If I replace the Destroy() with Update() then the dialog is correctly closed automatically:
progress_dlg = wx.ProgressDialog("Model Manager", "Connecting...", maximum=1)
progress_dlg.Pulse()
sleep(5)
progress_dlg.Update(1)
Whilst I can solve my problem, I would like to understand if this is the right way to do it & why Destroy() is not working?
Thanks in advance for any advice.
Gabbo CH
Solution 1:[1]
I don't think that Destroy should be called on the ProgressDialog. Update() with the maximum value is the right way. (With style PD_AUTO_HIDE, which is the default.) Afterwards, delete the reference to the dialog. Best way is to use the context manager.
If you look at the wxPython demo and the wxWidgets C++ examples, there's no Show(), ShowModal() or Destroy(). Recently there was a related ticket for wxPython where I have also added a usage example.
https://github.com/wxWidgets/Phoenix/issues/1610#issuecomment-1090664834
https://docs.wxwidgets.org/trunk/classwx_generic_progress_dialog.html
Solution 2:[2]
As Dietmar pointed you in the right direction, I'll concentrate on the other issues.
You code doesn't create the wx MainLoop and you are not allowing for the gauge to be visually updated. While your code may freakishly work in that small code sample, there will be issues later on, when the code increases.
Here's your code with minor changes. It includes setting the MainLoop and falling back to that loop during the long task to allow the progress gauge to update.
import wx
from time import sleep
def main():
progress_dlg = wx.ProgressDialog("Model Manager", "Connecting...", maximum=100)
for i in range(5):
progress_dlg.Pulse()
sleep(1) # some long tasks are done here
wx.GetApp().Yield()
progress_dlg.Update(100)
print("waiting")
wx.GetApp().Yield()
sleep(5)
extracted_names = ["ONE", "TWO", "THREE"]
CANDialog = wx.SingleChoiceDialog(None, "Please select:", "Selection", extracted_names)
CANDialog.SetSelection(0)
if CANDialog.ShowModal() == wx.ID_OK:
choosen_can = CANDialog.GetSelection()
else:
return
print(extracted_names[choosen_can])
if __name__ == '__main__':
app = wx.App()
main()
app.MainLoop()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dietmar Schwertberger |
| Solution 2 |
