Handling Resource Cleanup when closing modal dialog/Proper place to destroy dialog

Hi, I have a following question:

I have a modal dialog, in which I would like to perform cleanup of resources, when
dialog is getting closed (something like disconnecting of listeners and so on)

I’m currently doing it in the handler of wx.EVT_CLOSE

Therefore, I have two questions:

  1. Is it a correct place for doing clean-up of resources, connected to dialog?
  2. When reading documentation at wx.CloseEvent — wxPython Phoenix 4.2.0 documentation,
    it is written, that if CloseEvent can not be vetoed, then dialog should be destroyed or event should be skipped.

At the same time, in the documentation it is written, that one should destroy modal dialog windows, in order to free up underlying operating system resources.

If one tries to call get modal dialog, then the following code is recommended:

dlg = SomeDialog()
result = dlg.ShowModal()

… possibly some processing code for getting results from dialog

dlg.Destroy()

What is getting me confused is the question, is that where does dialog window
is getting destroyed in case, when modal dialog has EVT_CLOSE handler?
What is interaction between destruction of dialog in EVT_CLOSE handler and
in dlg.Destroy()? Where will the dialog be destroyed actually?

Many Thanks in Advance

Serhiy Yevtushenko

Hi Serhiy,

The wx.EVT_CLOSE handler is called when the button is pressed, but not for other buttons like YES, NO, OK, CANCEL that close the dialog.
I think the usual practice is to prompt YES/NO or OK/CANCEL with the associated ID’s buttons and do something depending on the return value. For example:

dlg = SomeDialog()
result = dlg.ShowModal()
if result == wx.ID_OK:
    # … possibly some processing code for getting results from dialog
    ...
dlg.Destroy()

Dialogs are not destroyed automatically because one may have to access the attributes even after closing the window. So, you have to call Destroy manually.
If you use the with statement, it will be automatically destroyed:

with SomeDialog() as dlg:
    if dlg.ShowModal() == wx.ID_OK:
    # … possibly some processing code for getting results from dialog
        ...

Hi, Kazuya.

Thank you for your reply.

For me several questions remain unclear:

If I open the modal dialog button, and it has no other means to close, except using [X] button on window (or pressing esc, which in turn calls dlg.Close),
and dialog has OnClose method, handling EVT_CLOSE where does actual dialog will be destroyed:

  • in the OnClose method?

or in the place, where dialog was actually created, if one suggest, that recommended code for handling dialog will be used, like
dlg = None
try:
dlg = CustomDialog()
res=dlg.ShowModal()
… some other actions
finally:
dlg.Destroy()

And in case, if such code is used, how would the dialog handle call to Destroy, which would be done twice (Once in the dialog OnClose method), and second time in the finally handler (or in context manager exit handler, if with statement is used)

What is the correct way to handle modal dialog, which has EVT_CLOSE handler:

  • perform destruction of dialog in the OnClose statement (which is required according to the documentation) and not to use dlg.Destroy in the code, which creates Modal Dialog
  • or to perform destruction of dialog in the finally statement after using the dialog
  • or redefine handling of [x] button in some way, that it will not be calling EVT_CLOSE, but will be doing
    dlg.EndModal(wx.ID_OK) or something similar ? (if this could be done at all)?

Many thanks in advance

Serhiy

Maybe, no problem. But I use the with statement all the time, so I’ve never done that.
However, if the dialog is opened in modeless mode, I think you should do something like this:

    def OnClose(self, evt):
        self.Destroy()
        evt.Skip()

because Dialog does not call Destroy automatically even if the CloseEvent is skipped.
(I’ve never encountered a situation where I had to use modeless dialogs :slight_smile:)

I guess using try-finally is the same as with-statement.

I’m not sure, but as far as I tested, calling Destroy multiple times seems like no problem unlike built-in dialogs such as wx.MessageDialog.

Please try the following code:

Code Example (click to expand)
import wx

class TestDialog(wx.Dialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.Bind(wx.EVT_CLOSE, self.OnClose)
    
    def OnClose(self, evt):
        self.Destroy()
        evt.Skip()

class TestFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        btn2 = wx.Button(self, label="Hello, wxPython")
        btn2.Bind(wx.EVT_BUTTON, self.OnTestButton)

    def OnTestButton(self, evt):
        dlg = wx.Dialog(self, title='Hello wxpython!')
        print(dlg.ShowModal()) # -> ID_CANCEL(5101)
        dlg.Destroy()
        
        print(bool(dlg)) # -> True
        wx.CallLater(1000,
            lambda: print(bool(dlg))) # -> False

if __name__ == "__main__":
    app = wx.App()
    frm = TestFrame(None)
    frm.Show()
    app.MainLoop()

Right after calling Destroy, the dialog object is still alive, but after some milliseconds pass, it will go garbage.
I’m not sure what causes the difference in behavior between the custom dialog and built-in dialogs. An expert may be able to explain this.

Hi, Kazuya.

Many thanks for your replies.

My original question has appeared while trying to understand the reason for failures, which are observed during production usage of the wxPython application.
The further investigation of the issue has led to the creation of my next topic:

Thank you one more time for your replies

Serhiy