Return value from wxPython Frame

Because the wxFrame has events that process via the app.MainLoop() functionality, the only way to get at the return value of a wx.Frame() is via catching an event.

The standard practice of handling events is typically from within the class which derives from wx.Window itself (e.g., Frame, Panel, etc.). Since you want code exterior to the wx.Frame to receive information that was gathered upon processing the OnClose() event, then the best way to do that is to register an event handler for your frame.

The documentation for wx.Window::PushEventHandler is probably the best resource and even the wxpython wiki has a great article on how to do this. Within the article, they register a custom handler which is an instance of "MouseDownTracker." Rather than instantiating within the PushEventHandler call, you'd want to instantiate it prior to the call so that you can retain a handle to the EventHandler derived class. That way, you can check on your derived EventHandler class-variables after the Frame has been destroyed, or even allow that derived class to do special things for you.

Here is an adaptation of that code from the wx python wiki (admittedly a little convoluted due to the requirement of handling the results of a custom event with a "calling" function):

import sys
import wx
import wx.lib.newevent
    
(MyCustomEvent, EVT_CUSTOM) = wx.lib.newevent.NewEvent()
    
class CustomEventTracker(wx.EvtHandler):
    def __init__(self, log, processingCodeFunctionHandle):
        wx.EvtHandler.__init__(self)
        self.processingCodeFunctionHandle = processingCodeFunctionHandle
        self.log = log
        EVT_CUSTOM(self, self.MyCustomEventHandler)
    
    def MyCustomEventHandler(self, evt):
        self.log.write(evt.resultOfDialog + '\n')
        self.processingCodeFunctionHandle(evt.resultOfDialog)
        evt.Skip()
    
class MyPanel2(wx.Panel):
    def __init__(self, parent, log):
        wx.Panel.__init__(self, parent)
        self.log = log
    
    def OnResults(self, resultData):
        self.log.write("Result data gathered: %s" % resultData)
    
class MyFrame(wx.Frame):
    def __init__(self, parent, ID=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
        wx.Frame.__init__(self, parent, ID, title, pos, size, style)
        self.panel = panel = wx.Panel(self, -1, style=wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.FULL_REPAINT_ON_RESIZE)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add((25, 25))
    
        row = wx.BoxSizer(wx.HORIZONTAL)
        row.Add((25,1))
        m_close = wx.Button(self.panel, wx.ID_CLOSE, "Close")
        m_close.Bind(wx.EVT_BUTTON, self.OnClose)
        row.Add(m_close, 0, wx.ALL, 10)
        sizer.Add(row)
        self.panel.SetSizer(sizer)
    
    def OnClose(self, evt):
        dlg = wx.MessageDialog(self, "Do you really want to close this frame?", "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
        result = dlg.ShowModal()
        dlg.Destroy()
        if result == wx.ID_CANCEL:
            event = MyCustomEvent(resultOfDialog="User Clicked CANCEL")
            self.GetEventHandler().ProcessEvent(event)
        else: # result == wx.ID_OK
            event = MyCustomEvent(resultOfDialog="User Clicked OK")
            self.GetEventHandler().ProcessEvent(event)
        self.Destroy()
    
app = wx.App(False)
f2 = wx.Frame(None, title="Frame 1 (for feedback)", size=(400, 350))
p2 = MyPanel2(f2, sys.stdout)
f2.Show()
eventTrackerHandle = CustomEventTracker(sys.stdout, p2.OnResults)
f1 = MyFrame(None, title="PushEventHandler Tester (deals with on close event)", size=(400, 350))
f1.PushEventHandler(eventTrackerHandle)
f1.Show()
app.MainLoop()

You can get the result of clicking the OK, CANCEL buttons from the Dialog ShowModal method.
Given dialog is an instance of one of the wxPython Dialog classes:

result = dialog.ShowModal()
if result == wx.ID_OK:
    print "OK"
else:
    print "Cancel"
dialog.Destroy()

A few years late for the initial question, but when looking for the answer to this question myself, I stumbled upon a built-in method of getting a return value from a modal without messing with any custom event funniness. Figured I'd post here in case anyone else needs it.

It's simply this guy right here:

wxDialog::EndModal void EndModal(int retCode)

Ends a modal dialog, passing a value to be returned from the *wxDialog::ShowModal invocation.*

Using the above, you can return whatever you want from the Dialog.

An example usage would be subclassing a wx.Dialog, and then placing the EndModal function in the button handlers.

class ProjectSettingsDialog(wx.Dialog):
    def __init__(self):
        wx.Dialog.__init__(self, None, -1, "Project Settings", size=(600,400))

        sizer = wx.BoxSizer(wx.VERTICAL) #main sized
        sizer.AddStretchSpacer(1) 

        msg = wx.StaticText(self, -1, label="This is a sample message")
        sizer.Add(msg, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 15)


        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
        okButton = wx.Button(self, -1, 'OK')
        self.Bind(wx.EVT_BUTTON, self.OnOK, okButton)

        cancelBtn = wx.Button(self, -1, "Cancel")
        self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelBtn)

        horizontal_sizer.Add(okButton, 0, wx.ALIGN_LEFT)
        horizontal_sizer.AddStretchSpacer(1)
        horizontal_sizer.Add(cancelBtn, 0, wx.ALIGN_RIGHT)

        sizer.Add(horizontal_sizer, 0)

        sizer.AddStretchSpacer(1)
            self.SetSizer(sizer)

    def OnOK(self, event):
        self.EndModal(wx.ID_OK) #returns numeric code to caller
        self.Destroy()


    def OnCancel(self, event):
        self.EndModal(wx.ID_CANCEL) #returns numeric code to caller
        self.Destroy()

(Note: I just banged this code out quickly; didn't test the sizers)

As you can see, all you need to do is call the EndModal from a button event to return a value to whatever spawned the dialog.