Creating Wizards for wmPython using wxGlade
wxGlade is a great tool for visual development of wxPython GUIs, but (at time of writing this page), is missing some vital features, such as support for wizards.
This page provides an easy recipe for using wxGlade to build the required panels of a wizard, then using a simple wizard subclass to build these panels into the wizard.
Process Overview
The basic steps in this recipe are:
- Create a dummy frame in wxGlade, consisting of a notebook which is populated with panels (each panel being a page to be added to our final wizard)
- Instantiate a wx.Wizard subclass (code provided below) to create a wizard
- Populate this wizard with the pages from the notebook we created in wxGlade
Creating Wizard Pages in wxGlade
Presently, wxGlade has no support for wizards, but it does support notebooks.
We will create a notebook within a dummy frame, and populate it with two or more pages, where each page will ultimately end up in our wizard.
Start up wxGlade, and follow these steps:
- Starting out 1.1. Create a new frame 1.2. Within the sizer that wxGlade automatically adds, insert a Notebook 1.3. Within the design window, you should see a single notebook tab
- Adding pages 2.1. Click on the notebook within the wxglade 'tree' window 2.2. Click on the 'widget' tab in the wxglade 'properties' window 2.3. Using the 'add' button, create as many tabs as you need, one for each page you want in your wizard 2.4. For each tab you create, choose a name which is a legal python identifier, and is meaningful for the page 2.5. Populate the body of each notebook pane with the widgets you need. 2.6. Click on the 'apply' button when done
- Creating panel classes 3.1. For each tab you have just created:
- 3.1.1. Click on that tab in the design window 3.1.2. Click on the body of the pane 3.1.3. Within the 'properties' window:
- 3.1.3.1. Click on the 'common' tab 3.1.3.2. Change the 'name' to the python identifier you chose
3.1.3.3. Change the 'class' from 'wxPanel' to a legal and meaningful python class identifier such as 'MyWizard_page1'
- 3.1.3.1. Click on the 'common' tab 3.1.3.2. Change the 'name' to the python identifier you chose
- 3.1.1. Click on that tab in the design window 3.1.2. Click on the body of the pane 3.1.3. Within the 'properties' window:
- Generate the code
- 4.1. Save the wxglade file 4.2. Generate python code (ctrl-g)
- Add the wizard subclass to your code:
1 class GladeWizard(wx.wizard.Wizard):
2 """
3 Wizard that can be built up from panel classes
4 generated by wxGlade
5 """
6 def __init__(self, parent, id=-1,
7 title=wx.EmptyString,
8 bitmap=wx.NullBitmap,
9 *args, **kw):
10
11 wx.wizard.Wizard.__init__(self, parent, id, title, bitmap, *args, **kw)
12 self.prevPanel = None
13
14 def addPages(self, pages):
15 """
16 Builds this wizard up from a set of panel classes generated
17 by wxGlade
18
19 Args:
20 - pages - a sequence of (name, cls) tuples, specifying each
21 page of the wizard.
22
23 Each 'name' in the tuple is the attribute under which the corresponding
24 page of the wizard will be stored in this wizard object as an attribute.
25
26 Each 'cls' in the tuple is a Panel subclass, as generated by wxGlade
27
28 Wizard creation recipe:
29 1. create a dummy frame in wxglade
30 2. insert a notebook into that frame's sizer
31 3. for each desired page of the wizard,
32 insert a pane into the notebook, and give it a unique class name
33 4. instantiate this wizard class, then call this method
34 with a list of (name, panelClass) tuples
35 """
36 for name, pageClass in pageClasses:
37 self.addPage(name, pageClass)
38 return
39
40 def addPage(self, attrname, panelClass):
41 """
42 Adds a panel to this wizard
43
44 Arguments:
45 - attrname - attribute under which to save a ref to the
46 instantiated panel within the wizard abject
47 - panelClass - a subclass of wx.Panel, to add to the new
48 page of the wizard
49 """
50 # create wizard page object, and add on
51 # instance of our panel class to it
52 page = wx.wizard.WizardPageSimple(self)
53 page.sizer = wx.BoxSizer(wx.VERTICAL)
54 page.SetSizer(page.sizer)
55 pageInst = panelClass(page)
56 page.sizer.Add(pageInst, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
57
58 # save the panel instance as named attribute
59 setattr(self, attrname, pageInst)
60
61 # chain to previous, if this is not the first page
62 if self.prevPanel:
63 wx.wizard.WizardPageSimple_Chain(self.prevPanel, page)
64 else:
65 self.firstPanel = page
66
67 # remember this as 'previous' page, for adding/chaining the next
68 self.prevPanel = page
69
70 def run(self):
71 return self.RunWizard(self.firstPanel)
Creating and using the wizard in your program
Assuming you have set wxglade to generate its python code in 'mygui.py', add to your python program the line:
1 import mygui as gui
Within your wxApp code, possibly within your .OnInit() method, use code such as:
If you have a bitmap, you could add it via a 'bitmap=' keyword to the GladeWizard constructor.
With the example above:
Our wizard has two pages, present in the wxGlade generated file as WizardPane_1 and WizardPane_2
- Instances of these pane classes will be stored in the wizard object as attributes 'pane1' and 'pane2' respectively
- We can easily address each of the widgets on each page of the wizard. For example:
1 self.maxRetries = int(self.wizard.pane1.fld_maxRetries.GetValue())
Conclusion
This is a hack, and will hopefully be obsolete soon (when the wxGlade developers add wizard support).
While it might seem cumbersome, it's probably not much more complicated than it will be when full wizard support is finally added.