Matthew Connolly Matthew Connolly - 3 months ago 19
Python Question

wxPython return value from Wizard to calling Frame

My problem is as follows:

I am designing a wizard for the construction of an object to be added to a list of objects in the calling frame of my program. At the end of the wizard I would like to pass the newly created object back to the calling frame to be inserted into the list. In order to simulate this basic functionality on an abstract basis I have constructed the following, scaled down app:

mainframe.py

import wx
import wiz_test
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title="Main")
panel = wx.Panel(self)
callButton = wx.Button(panel, label = "Call Wizard")
callButton.Bind(wx.EVT_BUTTON,self.launchWizard)
self.Show()
def launchWizard(self,event):
wiz = wiz_test.WizObj(self)
a = 0
if wiz == wx.wizard.EVT_WIZARD_FINISHED:
a = wiz.answer
print a

if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()


wiz_test.py

import wx
import wx.wizard as wiz

class WizPage(wiz.WizardPageSimple):
def __init__(self, parent):
self.answer = 3
wiz.WizardPageSimple.__init__(self, parent)

sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)

title = wx.StaticText(self, -1, "Wizard Page")
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))

sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)


class WizObj(object):
def __init__(self,parent):
wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")
page1 = WizPage(wizard)
wizard.FitToPage(page1)
wizard.RunWizard(page1)
wizard.Destroy()

if __name__ == "__main__":
app = wx.App(False)
main()
app.MainLoop()


The ultimate goal in this small example is to get the MainFrame instance to output the value '3' derived from the .answer member variable of the WizObj instance when the wx.wizard.EVT_WIZARD_FINISHED event is triggered. However it is clearly not working at this point as the current code only returns '0'. Am I approaching this the correct way? Should I be binding the EVT_WIZARD_FINISHED event instead, and if so, how would I access that from Mainframe?

Answer

I was able to solve this problem through the use of the "pubsub" capability within the wxPython library. Specifically, I added a pub.subscribe() instance immediately prior to the instantiation of my wizard within the calling frame. Inside of the wizard I pass the value via pub.sendMessage() just before destroying the wizard. It is important to note that the pass value had to be specified in order for the pubsub send would work effectively.

The following code is the modified version of the original code which now functions.

MainFrame.py

import wx
import wiz_test
from wx.lib.pubsub import pub

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,None,title="Main")
        panel = wx.Panel(self)
        callButton = wx.Button(panel, label = "Call Wizard")
        callButton.Bind(wx.EVT_BUTTON,self.launchWizard)
        self.Show()

    def catch_stuff(self,a):
        print a

    def launchWizard(self,event):
        pub.subscribe(self.catch_stuff,'valPass')
        wiz = wiz_test.WizObj(self,a)



if __name__ == '__main__':
    app = wx.App(False)
    frame = MainFrame()
    app.MainLoop()

wiz_test.py

import wx
import wx.wizard as wiz
from wx.lib.pubsub import pub

class WizPage(wiz.WizardPageSimple):
    def __init__(self, parent):
        self.answer = 3
        wiz.WizardPageSimple.__init__(self, parent)

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)

        title = wx.StaticText(self, -1, "Wizard Page")
        title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))


        sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
        sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)

#----------------------------------------------------------------------
class WizObj(object):
    def __init__(self,parent,a):
        wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")
        page1 = WizPage(wizard)
        wizard.FitToPage(page1)
        wizard.RunWizard(page1)
        pub.sendMessage('valPass',a = page1.answer)
        wizard.Destroy()

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    main()
    app.MainLoop()

The result is that the console prints the value 3 which was retrieved from the called wizard.

Comments