wxPython: A Simple Notebook Example

The other day, I received a complaint that my original notebook example in my Book control series was too complicated. I don’t really write just n00b-friendly articles and never claimed to, but this comment rankled, so I decided to write a super simple example for the wxPython newbies. I hope you like it!

import random
import wx

########################################################################
class TabPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """"""
        wx.Panel.__init__(self, parent=parent)
        
        colors = ["red", "blue", "gray", "yellow", "green"]
        self.SetBackgroundColour(random.choice(colors))
        
        btn = wx.Button(self, label="Press Me")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(btn, 0, wx.ALL, 10)
        self.SetSizer(sizer)

########################################################################
class DemoFrame(wx.Frame):
    """
    Frame that holds all other widgets
    """

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""        
        wx.Frame.__init__(self, None, wx.ID_ANY, 
                          "Notebook Tutorial",
                          size=(600,400)
                          )
        panel = wx.Panel(self)
        
        notebook = wx.Notebook(panel)
        tabOne = TabPanel(notebook)
        notebook.AddPage(tabOne, "Tab 1")
        
        tabTwo = TabPanel(notebook)
        notebook.AddPage(tabTwo, "Tab 2")
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
        panel.SetSizer(sizer)
        self.Layout()
        
        self.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = DemoFrame()
    app.MainLoop()

Normally when I write a wx.Notebook example, I want each tab to look different in some way. If you look at the first article in that previously mentioned series, you will see some complex widgets embedded in each page of the notebook. However, for this example, I just make each page a different color, at random. Let’s take a moment and unpack this code.

In the DemoFrame class, we have a main panel which is the only child widget of the Frame. Inside that, we have the Notebook control. Each page of the Notebook is an instance of our TabPanel class, which should have a “random” background color and one button that doesn’t do anything. We add the Notebook to a sizer and set it to expand with a proportion of 1. This means that it will fill the panel, and since the panel fills the frame, the notebook will fill the frame too. To be honest, that’s really all there is to it.

Another topic of note is that the Notebook events, such as EVT_NOTEBOOK_PAGE_CHANGED, may need to have an “event.Skip()” call in their event handler to make them function properly. The event hierarchy in wxPython is a little hard to grasp, but think of it as air bubbles in a pond. If you bind a widget to a particular event and don’t call Skip(), then your event is handled ONLY in that particular handler. This is like having the bubble popped part way from the bottom of the pond. However, occasionally you’ll need the event to be handled higher up, like in the widget’s parent or grandparent. If so, call Skip() and your event “bubble” will rise to the next handler. The wxPython wiki has more on this as does the “wxPython in Action” book.

Well, that covers the simple Notebook example. If you need to know more about the Notebook or the other book controls, see the first link in this article or download the wxPython Demo.