Getting started with wxPython

A First Application: "Hello, World"

As is traditional, we are first going to write a Small "Hello, world" application. Here is the code:

   1 #!/usr/bin/env python
   2 import wx
   3 
   4 app = wx.App(False)  # Create a new app, don't redirect stdout/stderr to a window.
   5 frame = wx.Frame(None, wx.ID_ANY, "Hello World") # A Frame is a top-level window.
   6 frame.Show(True)     # Show the frame.
   7 app.MainLoop()

Explanations:

app = wx.App(False)

Every wxPython app is an instance of wx.App. For most simple applications you can use wx.App as is. When you get to more complex applications you may need to extend the wx.App class. The "False" parameter means "don't redirect stdout and stderr to a window".

wx.Frame(None,wx.ID_ANY,"Hello")

A wx.Frame is a top-level window. The syntax is x.Frame(Parent, Id, Title). Most of the constructors have this shape (a parent object, followed by an Id). In this example, we use None for "no parent" and wx.ID_ANY to have wxWidgets pick an id for us.

frame.Show(True)

We make a frame visible by "showing" it.

app.MainLoop()

Finally, we start the application's MainLoop whose role is to handle the events.

Note: You almost always want to use wx.ID_ANY or another standard ID (v2.8) provided by wxWidgets. You can make your own IDs, but there is no reason to do that.

Run the program and you should see a window like this one:

Windows or Frames?

When people talk about GUIs, they usually speak of windows, menus and icons. Naturally then, you would expect that wx.Window should represent a window on the screen. Unfortunately, this is not the case. A wx.Window is the base class from which all visual elements are derived (buttons, menus, etc) and what we normally think of as a program window is a wx.Frame. This is an unfortunate inconsistency that has led to much confusion for new users.

Building a simple text editor

In this tutorial we are going to build a simple text editor. In the process, we will explore several widgets, and learn about features such as events and callbacks.

First steps

The first step is to make a simple frame with an editable text box inside. A text box is made with the wx.TextCtrl widget. By default, a text box is a single-line field, but the wx.TE_MULTILINE parameter allows you to enter multiple lines of text.

   1 #!/usr/bin/env python
   2 import wx
   3 class MyFrame(wx.Frame):
   4     """ We simply derive a new class of Frame. """
   5     def __init__(self, parent, title):
   6         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   7         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   8         self.Show(True)
   9 
  10 app = wx.App(False)
  11 frame = MyFrame(None, 'Small editor')
  12 app.MainLoop()

In this example, we derive from wx.Frame and overwrite its __init__ method. Here we declare a new wx.TextCtrl which is a simple text edit control. Note that since the MyFrame runs self.Show() inside its __init__ method, we no longer have to call frame.Show() explicitly.

Adding a menu bar

Every application should have a menu bar and a status bar. Let's add them to ours:

   1 import wx
   2 
   3 class MainWindow(wx.Frame):
   4     def __init__(self, parent, title):
   5         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   6         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   7         self.CreateStatusBar() # A Statusbar in the bottom of the window
   8 
   9         # Setting up the menu.
  10         filemenu= wx.Menu()
  11 
  12         # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
  13         filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  14         filemenu.AppendSeparator()
  15         filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  16 
  17         # Creating the menubar.
  18         menuBar = wx.MenuBar()
  19         menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
  20         self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
  21         self.Show(True)
  22 
  23 app = wx.App(False)
  24 frame = MainWindow(None, "Sample editor")
  25 app.MainLoop()

TIP: Notice the wx.ID_ABOUT and wx.ID_EXIT ids. These are standard ids provided by wxWidgets (see a full list here). It is a good habit to use the standard ID if there is one available. This helps wxWidgets know how to display the widget in each platform to make it look more native.

Event handling

Reacting to events in wxPython is called event handling. An event is when "something" happens on your application (a button click, text input, mouse movement, etc). Much of GUI programming consists of responding to events. You bind an object to an event using the Bind() method:

   1 class MainWindow(wx.Frame):
   2     def __init__(self, parent, title):
   3         wx.Frame.__init__(self,parent, title=title, size=(200,100))
   4         ...
   5         menuItem = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
   6         self.Bind(wx.EVT_MENU, self.OnAbout, menuItem)

This means that, from now on, when the user selects the "About" menu item, the method self.OnAbout will be executed. wx.EVT_MENU is the "select menu item" event. wxWidgets understands many other events (see the full list). The self.OnAbout method has the general declaration:

   1 def OnAbout(self, event):
   2         ...

Here event is an instance of a subclass of wx.Event. For example, a button-click event - wx.EVT_BUTTON - is a subclass of wx.Event.

The method is executed when the event occurs. By default, this method will handle the event and the event will stop after the callback finishes. However, you can "skip" an event with event.Skip(). This causes the event to go through the hierarchy of event handlers. For example:

   1 def OnButtonClick(self, event):
   2     if (some_condition):
   3         do_something()
   4     else:
   5         event.Skip()
   6 
   7 def OnEvent(self, event):
   8     ...

When a button-click event occurs, the method OnButtonClick gets called. If some_condition is true, we do_something() otherwise we let the event be handled by the more general event handler. Now let's have a look at our application:

   1 import os
   2 import wx
   3 
   4 
   5 class MainWindow(wx.Frame):
   6     def __init__(self, parent, title):
   7         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   8         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   9         self.CreateStatusBar() # A StatusBar in the bottom of the window
  10 
  11         # Setting up the menu.
  12         filemenu= wx.Menu()
  13 
  14         # wx.ID_ABOUT and wx.ID_EXIT are standard ids provided by wxWidgets.
  15         menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  16         menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  17 
  18         # Creating the menubar.
  19         menuBar = wx.MenuBar()
  20         menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
  21         self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
  22 
  23         # Set events.
  24         self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
  25         self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
  26 
  27         self.Show(True)
  28 
  29     def OnAbout(self,e):
  30         # A message dialog box with an OK button. wx.OK is a standard ID in wxWidgets.
  31         dlg = wx.MessageDialog( self, "A small text editor", "About Sample Editor", wx.OK)
  32         dlg.ShowModal() # Show it
  33         dlg.Destroy() # finally destroy it when finished.
  34 
  35     def OnExit(self,e):
  36         self.Close(True)  # Close the frame.
  37 
  38 app = wx.App(False)
  39 frame = MainWindow(None, "Sample editor")
  40 app.MainLoop()

Note: Instead of

   1 wx.MessageDialog( self, "A small editor in wxPython", "About Sample Editor", wx.OK)

We could have omitted the id. In that case wxWidget automatically generates an ID (just as if you specified wx.ID_ANY):

   1 wx.MessageDialog( self, "A small editor in wxPython", "About Sample Editor")

Dialogs

Of course an editor is useless if it is not able to save or open documents. That's where Common dialogs come in. Common dialogs are those offered by the underlying platform so that your application will look exactly like a native application. Here is the implementation of the OnOpen method in MainWindow:

   1     def OnOpen(self,e):
   2         """ Open a file"""
   3         self.dirname = ''
   4         dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
   5         if dlg.ShowModal() == wx.ID_OK:
   6             self.filename = dlg.GetFilename()
   7             self.dirname = dlg.GetDirectory()
   8             f = open(os.path.join(self.dirname, self.filename), 'r')
   9             self.control.SetValue(f.read())
  10             f.close()
  11         dlg.Destroy()

Explanation:

You should now be able to add the corresponding entry into the menu and connect it to the OnOpen method. If you have trouble, scroll down to the appendix for the full source code.

Possible extensions

Of course, this program is far from being a decent editor. But adding other features should not be any more difficult than what has already been done. You might take inspiration from the demos that are bundled with wxPython:

Working with Windows

In this section, we are going to present the way wxPython deals with windows and their contents, including building input forms and using various widgets/controls. We are going to build a small application that calculates the price of a quote. If you are already an experienced GUI developer, this is going to be easy and you might want to move on to the Boa-Constructor Subsection in the Advanced topics chapter.

Overview

Laying out Visual Elements

Sizers

   1 import wx
   2 import os
   3 
   4 class MainWindow(wx.Frame):
   5     def __init__(self, parent, title):
   6         self.dirname=''
   7 
   8         # A "-1" in the size parameter instructs wxWidgets to use the default size.
   9         # In this case, we select 200px width and the default height.
  10         wx.Frame.__init__(self, parent, title=title, size=(200,-1))
  11         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
  12         self.CreateStatusBar() # A Statusbar in the bottom of the window
  13 
  14         # Setting up the menu.
  15         filemenu= wx.Menu()
  16         menuOpen = filemenu.Append(wx.ID_OPEN, "&Open"," Open a file to edit")
  17         menuAbout= filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  18         menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  19 
  20         # Creating the menubar.
  21         menuBar = wx.MenuBar()
  22         menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
  23         self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
  24 
  25         # Events.
  26         self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
  27         self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
  28         self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
  29 
  30         self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
  31         self.buttons = []
  32         for i in range(0, 6):
  33             self.buttons.append(wx.Button(self, -1, "Button &"+str(i)))
  34             self.sizer2.Add(self.buttons[i], 1, wx.EXPAND)
  35 
  36         # Use some sizers to see layout options
  37         self.sizer = wx.BoxSizer(wx.VERTICAL)
  38         self.sizer.Add(self.control, 1, wx.EXPAND)
  39         self.sizer.Add(self.sizer2, 0, wx.EXPAND)
  40 
  41         #Layout sizers
  42         self.SetSizer(self.sizer)
  43         self.SetAutoLayout(1)
  44         self.sizer.Fit(self)
  45         self.Show()
  46 
  47     def OnAbout(self,e):
  48         # Create a message dialog box
  49         dlg = wx.MessageDialog(self, " A sample editor \n in wxPython", "About Sample Editor", wx.OK)
  50         dlg.ShowModal() # Shows it
  51         dlg.Destroy() # finally destroy it when finished.
  52 
  53     def OnExit(self,e):
  54         self.Close(True)  # Close the frame.
  55 
  56     def OnOpen(self,e):
  57         """ Open a file"""
  58         dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
  59         if dlg.ShowModal() == wx.ID_OK:
  60             self.filename = dlg.GetFilename()
  61             self.dirname = dlg.GetDirectory()
  62             f = open(os.path.join(self.dirname, self.filename), 'r')
  63             self.control.SetValue(f.read())
  64             f.close()
  65         dlg.Destroy()
  66 
  67 app = wx.App(False)
  68 frame = MainWindow(None, "Sample editor")
  69 app.MainLoop()

   1 window.SetSizer(sizer)
   2 window.SetAutoLayout(True)
   3 sizer.Fit(window)

Validators

A Working Example

Our first label within a panel

   1 import wx
   2 class ExampleFrame(wx.Frame):
   3     def __init__(self, parent):
   4         wx.Frame.__init__(self, parent)
   5         panel = wx.Panel(self)
   6         self.quote = wx.StaticText(panel, label="Your quote: ", pos=(20, 30))
   7         self.Show()
   8 
   9 app = wx.App(False)
  10 ExampleFrame(None)
  11 app.MainLoop()

   1         self.quote = wx.StaticText(panel, label="Your quote: ", pos=(20, 30))

[7] According to wxPython documentation:

[8] A label is used to display text that is not supposed to interact with the user.

Adding a few more controls

   1 clearButton = wx.Button(self, wx.ID_CLEAR, "Clear")
   2 self.Bind(wx.EVT_BUTTON, self.OnClear, clearButton)

   1 textField = wx.TextCtrl(self)
   2 self.Bind(wx.EVT_TEXT, self.OnChange, textField)
   3 self.Bind(wx.EVT_CHAR, self.OnKeyPress, textField)

   1 import wx
   2 class ExamplePanel(wx.Panel):
   3     def __init__(self, parent):
   4         wx.Panel.__init__(self, parent)
   5         self.quote = wx.StaticText(self, label="Your quote :", pos=(20, 30))
   6 
   7         # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
   8         self.logger = wx.TextCtrl(self, pos=(300,20), size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)
   9 
  10         # A button
  11         self.button =wx.Button(self, label="Save", pos=(200, 325))
  12         self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
  13 
  14         # the edit control - one line version.
  15         self.lblname = wx.StaticText(self, label="Your name :", pos=(20,60))
  16         self.editname = wx.TextCtrl(self, value="Enter here your name", pos=(150, 60), size=(140,-1))
  17         self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
  18         self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
  19 
  20         # the combobox Control
  21         self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
  22         self.lblhear = wx.StaticText(self, label="How did you hear from us ?", pos=(20, 90))
  23         self.edithear = wx.ComboBox(self, pos=(150, 90), size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
  24         self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
  25         self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)
  26 
  27         # Checkbox
  28         self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?", pos=(20,180))
  29         self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
  30 
  31         # Radio Boxes
  32         radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
  33         rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
  34                          style=wx.RA_SPECIFY_COLS)
  35         self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)
  36 
  37     def EvtRadioBox(self, event):
  38         self.logger.AppendText('EvtRadioBox: %d\n' % event.GetInt())
  39     def EvtComboBox(self, event):
  40         self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
  41     def OnClick(self,event):
  42         self.logger.AppendText(" Click on object with Id %d\n" %event.GetId())
  43     def EvtText(self, event):
  44         self.logger.AppendText('EvtText: %s\n' % event.GetString())
  45     def EvtChar(self, event):
  46         self.logger.AppendText('EvtChar: %d\n' % event.GetKeyCode())
  47         event.Skip()
  48     def EvtCheckBox(self, event):
  49         self.logger.AppendText('EvtCheckBox: %d\n' % event.Checked())
  50 
  51 
  52 app = wx.App(False)
  53 frame = wx.Frame(None)
  54 panel = ExamplePanel(frame)
  55 frame.Show()
  56 app.MainLoop()

The notebook

Note how ExamplePanel's parent is the Notebook. This is important.

   1 app = wx.App(False)
   2 frame = wx.Frame(None, title="Demo with Notebook")
   3 nb = wx.Notebook(frame)
   4 
   5 
   6 nb.AddPage(ExamplePanel(nb), "Absolute Positioning")
   7 nb.AddPage(ExamplePanel(nb), "Page Two")
   8 nb.AddPage(ExamplePanel(nb), "Page Three")
   9 frame.Show()
  10 app.MainLoop()

Improving the layout - Using Sizers

Here is the sample above re-written to use sizers:

   1 class ExamplePanel(wx.Panel):
   2     def __init__(self, parent):
   3         wx.Panel.__init__(self, parent)
   4 
   5         # create some sizers
   6         mainSizer = wx.BoxSizer(wx.VERTICAL)
   7         grid = wx.GridBagSizer(hgap=5, vgap=5)
   8         hSizer = wx.BoxSizer(wx.HORIZONTAL)
   9 
  10         self.quote = wx.StaticText(self, label="Your quote: ")
  11         grid.Add(self.quote, pos=(0,0))
  12 
  13         # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
  14         self.logger = wx.TextCtrl(self, size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)
  15 
  16         # A button
  17         self.button =wx.Button(self, label="Save")
  18         self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
  19 
  20         # the edit control - one line version.
  21         self.lblname = wx.StaticText(self, label="Your name :")
  22         grid.Add(self.lblname, pos=(1,0))
  23         self.editname = wx.TextCtrl(self, value="Enter here your name", size=(140,-1))
  24         grid.Add(self.editname, pos=(1,1))
  25         self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
  26         self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
  27 
  28         # the combobox Control
  29         self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
  30         self.lblhear = wx.StaticText(self, label="How did you hear from us ?")
  31         grid.Add(self.lblhear, pos=(3,0))
  32         self.edithear = wx.ComboBox(self, size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
  33         grid.Add(self.edithear, pos=(3,1))
  34         self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
  35         self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)
  36 
  37         # add a spacer to the sizer
  38         grid.Add((10, 40), pos=(2,0))
  39 
  40         # Checkbox
  41         self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?")
  42         grid.Add(self.insure, pos=(4,0), span=(1,2), flag=wx.BOTTOM, border=5)
  43         self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
  44 
  45         # Radio Boxes
  46         radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
  47         rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
  48                          style=wx.RA_SPECIFY_COLS)
  49         grid.Add(rb, pos=(5,0), span=(1,2))
  50         self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)
  51 
  52         hSizer.Add(grid, 0, wx.ALL, 5)
  53         hSizer.Add(self.logger)
  54         mainSizer.Add(hSizer, 0, wx.ALL, 5)
  55         mainSizer.Add(self.button, 0, wx.CENTER)
  56         self.SetSizerAndFit(mainSizer)

This example uses a GridBagSizer to place its items. The "pos" argument controls where in the grid the widget is placed, in (x, y) positions. For instance, (0, 0) is the top-left position, whereas (3, 5) would be the third row down, and the fifth column across. The span argument allows a widget to span over multiple rows or columns.

Responding to User Actions

Overview

A Working Example

Drawing

Overview

A Working Example

Using wxPython

Debugging Techniques

   1 class MyApp (wx.App):
   2 #...
   3 #...
   4 #...
   5 myapp = MyApp() # functions normally. Stdio is redirected to its own window
   6 myapp = MyApp(0) #does not redirect stdout. Tracebacks will show up at the console.
   7 myapp = MyApp(1, 'filespec') #redirects stdout to the file 'filespec'
   8 # NOTE: These are named parameters, so you can do this for improved readability:
   9 myapp = MyApp(redirect = 1, filename = 'filespec') # will redirect stdout to 'filespec'
  10 myapp = MyApp(redirect = 0) #stdio will stay at the console...

You can also use the Widget Inspection Tool to help debug most layout issues.

TODO: Discuss source debuggers, event loop conflicts, etc.

PyCrust interactive shell

wxPython distribution comes with the nice PyCrust shell. With it you can interactvely test your layout. Here is a with a sample code.

Deploying your wxPython Application

Next Steps

Events

So then "self.Bind(wx.EVT_SOMETHING, ACallable)" would mean:

When a SOMETHING event is delivered to this Window (self), and it comes from any child window or itself, then call ACallable, and self must be a class derived from a wxPython window (e.g. a Button, a Dialog, a Frame), and "ACallable" can be any function, though the usual choice by the programmer is to make it a function within the above-mentioned class.

The second version "self.Bind(wx.EVT_SOMETHING, ACallable, srcWin)" means:

When a SOMETHING event is generated by "srcWin", and it comes up through the window hierarchy to this Window (self), then call ACallable.

But some events can only be caught in the Window in which they are generated (which means the second form then won't do anything), so it is best to use the first form whenever possible, which would be basically everywhere except for menu items, which don't have a Bind() method.

Scintilla

Boa-constructor

multi threading

Managed/ Non Managed windows

Useful resources

As a conclusion- slithering our way to the future of GUI-apps

You have now covered the main aspects of wxPython programming and should be able to start writing wxPython applications. Do not hesitate to take part in the wxPython community by subscribing to the mailing lists and by posting questions or answers on those lists.

Contributors

Appendix

Small editor - Complete Source

Building Forms - Complete source

Drawing with wxPython - Complete Source

A rudimentary project organizer - Complete Source

Frequently Asked Questions

Comments

Comment Sample

Feel free to add any remarks or comments here. Content suggestions are welcome as well as corrections...

OS independent path concatenation

This code:

   1 import os
   2 f=open(os.path.join(self.dirname,self.filename),'r')

...can still cause problems on Windows in cases where you've used the common dialog to navigate to the root dir of the drive. In that case, the common dialog function will return self.dirname='c:' (you'd expect 'c:\' unless you've dealt with MS for many years). Unfortunately, os.path.join won't join as expected when the string contains a ':', and so an error occurs when opening 'c:file.py', since this is dependent on the cwd setting (assuming 'file.py' exists in 'c:\', but not in your cwd).

Solutions? I use:

   1 f=open(self.dirname+'/'+self.filename,'r')

without any problems. I presume that lower level functions like "open" probably call os.path.normpath() which fixes it on Windows. The advantage to this is that I can use the same method ('/') for directories & URL's, and the code runs cross-platform Win/Linux. I'm sure it will get me someday, though. A better option would probably be:

   1 f=open(os.path.normpath(os.path.join(self.dirname+os.sep,self.filename)),'r')

I think this works for all cases except where self.dirname is 'c:\' before you start (normpath won't strip extra separators on the 1st join arg). Or perhaps an elegant solution using os.path.isabs() would be better.

Use the sizer in one step

I always thought setting sizer must be shorter than three lines an indeed it is (at least for wxWidgets/wxPython 2.4), the used above

   1 window.SetSizer(sizer)
   2 window.SetAutoLayout(true)
   3 sizer.Fit(window)

might be shorten to one line

   1 window.SetSizerAndFit(sizer)

How to get tabs to work

It's beeen really hard to me getting tabs to work in non-dialog windows: the only hint in the whole documentation is in wxWindow style wxTAB_TRAVERSAL description, but it's not so clear. What I finally got was:

1)For the tabbing to work at all, the window or individual panel you plonk controls/wigits on has to have as part of its style flag the following: wxTAB_TRAVERSAL ie;

   1 class ContactsPanel(wx.Panel):
   2     def __init__(self, parent,id):
   3         wx.Panel.__init__(self, parent, id, wx.DefaultPosition,wx.DefaultSize,
   4                          wx.RAISED_BORDER|wx.TAB_TRAVERSAL)

2) The tab order is set by the order you add controls to the panel or frame.

(information comes from)

3) Tabbing order also seems to be dependent in the order widgets are created. I assume this is due to widget ID numbers. Order of addition to sizers/panels did not seem to help me with #2. -- Keith Veleba

4) Here's a little idiom for setting tab order once you have the controls set up:

   1 order = (control1, control2, control3, ...)
   2 for i in xrange(len(order) - 1):
   3     order[i+1].MoveAfterInTabOrder(order[i])

(The list contains the actual control objects.) This makes it easy to change the order, add new controls, etc. -- Don Dwiggins

Getting Started (last edited 2013-12-29 17:33:24 by 108-239-49-128)