views:

259

answers:

5

Hi,

Could someone show me how I could return a value from a wxPython Frame? When the use clicks close, I popup a message dialog asking him a question. I would like to return the return code of this message dialog to my calling function.

Thanks

A: 

Check this answer on comp.lang.python: Linkie

Francisco P.
that link goes to a book on amazon about assembly language.
Bryan Oakley
What? My clipboard betrayed me. Imagine it was bestiality, instead. Argh.
Francisco P.
+1  A: 

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()
joaquin
A: 

I don't think a wxFrame can return a value since it is not modal. If you don't need to use a wxFrame, then a modal dialog could work for you. If you really need a frame, I'd consider using a custom event.

It would go something like this: (1) User clicks to close the wxFrame (2) You override OnClose (or something like that) to pop up a dialog to ask the user a question (3) Create and post the custom event (4) Close the wxFrame (5) Some other code processes your custom event

Jeff
+1  A: 

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()
David L Ernstrom
A: 

I think I just had the same problem as you. Instead of making that popup a frame, I made it a dialog instead. I made a custom dialog by inheriting a wx.dialog instead of a wx.frame. Then you can utilize the code that joaquin posted above. You check the return value of the dialog to see what was entered. This can be done by storing the value of the textctrl when the user clicks ok into a local variable. Then before it's destroyed, you get that value somehow.

The custom dialog section of this site helped me out greatly. http://zetcode.com/wxpython/dialogs/

Kevin