If you’re new to GUI programming (and wxPython in particular), you may not know what a “book” control is. It may be that other languages call this control something different too. In wxPython, a book control allows the user to switch between various panels. The most common examples are browsers and system option dialogs with tabbed interfaces. This article will walk you though the creation and basic configuration of these controls.

wxPython currently has seven of these controls built-in with an eighth in the agw AUI widget. I will cover the following widgets in this article (although not necessarily in this order) : AUI_Notebook, Choicebook, Listbook, Notebook, Toolbook, and Treebook. In the second article, I will cover the two agw book controls: FlatNotebook and the one embedded in the new agw AUI. Please note that this first post is a three page article. I personally don’t think the default WordPress pagination is very clear.

wx.Notebook

We will start with the most familiar book widget, the wx.Notebook. The following screenshot gives you an idea of what it should look like:

notebookDemo

Each notebook tab (or page) will be made with a wx.Panel object. I have created three different panel classes that we will use for the tabs. I have taken some code from the wxPython Demo for these panels (and the book widgets) and modified them for this tutorial. Let’s take a look at one of the panels to give you an idea of what I’m doing:

import wx
 
class TabPanel(wx.Panel):
    """
    This will be the first notebook tab
    """
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """"""
 
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
        txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txtOne, 0, wx.ALL, 5)
        sizer.Add(txtTwo, 0, wx.ALL, 5)
 
        self.SetSizer(sizer)

The code above will create a panel with two text controls inside a vertical BoxSizer. That is all it will do. Now let’s see how we can use this code in conjunction with a wx.Notebook. Here’s a simple demo:

import images
import wx
import panelOne, panelTwo, panelThree
 
########################################################################
class NotebookDemo(wx.Notebook):
    """
    Notebook class
    """
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style=
                             wx.BK_DEFAULT
                             #wx.BK_TOP 
                             #wx.BK_BOTTOM
                             #wx.BK_LEFT
                             #wx.BK_RIGHT
                             )
 
        # Create the first tab and add it to the notebook
        tabOne = panelOne.TabPanel(self)
        tabOne.SetBackgroundColour("Gray")
        self.AddPage(tabOne, "TabOne")
 
        # Show how to put an image on one of the notebook tabs,
        # first make the image list:
        il = wx.ImageList(16, 16)
        idx1 = il.Add(images.Smiles.GetBitmap())
        self.AssignImageList(il)
 
        # now put an image on the first tab we just created:
        self.SetPageImage(0, idx1)
 
        # Create and add the second tab
        tabTwo = panelTwo.TabPanel(self)
        self.AddPage(tabTwo, "TabTwo")
 
        # Create and add the third tab
        self.AddPage(panelThree.TabPanel(self), "TabThree")
 
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
 
    def OnPageChanged(self, event):
        old = event.GetOldSelection()
        new = event.GetSelection()
        sel = self.GetSelection()
        print 'OnPageChanged,  old:%d, new:%d, sel:%d\n' % (old, new, sel)
        event.Skip()
 
    def OnPageChanging(self, event):
        old = event.GetOldSelection()
        new = event.GetSelection()
        sel = self.GetSelection()
        print 'OnPageChanging, old:%d, new:%d, sel:%d\n' % (old, new, sel)
        event.Skip()
 
 
########################################################################
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 = NotebookDemo(panel)
        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.PySimpleApp()
    frame = DemoFrame()
    app.MainLoop()

Let’s break this down a bit. First, I create an instance of the wx.Notebook class and name it NotebookDemo. In its “__init__”, I have some styles commented out. These styles tell the notebook where to place the tabs (i.e. on the top, left, right or bottom). The default is to place them on the top. You can comment out the default and uncomment one of the other lines to see the difference.

To create the first tab, all we need to do is the following:

# Create the first tab and add it to the notebook
tabOne = panelOne.TabPanel(self)
tabOne.SetBackgroundColour("Gray")
self.AddPage(tabOne, "TabOne")

Actually, we don’t even need to set the background color, but I did that to make the text controls stand out a bit more. If we don’t set the color, we can make this into a one-liner like this:

self.AddPage(panelOne.TabPanel(self), "TabOne")

The AddPage() method is the primary way to add a page to a notebook widget. This method has the following arguments:

AddPage(self, page, text, select, imageId)

I only add the page and the text for the tab. If you want, you can pass “True” as the 4th argument and make that tab be selected. The first tab will be selected by default though, so doing that would be kind of silly. The fifth argument allows the programmer to add an image to the tab; however, we do that with the next bit of code:

# Show how to put an image on one of the notebook tabs,
# first make the image list:
il = wx.ImageList(16, 16)
idx1 = il.Add(images.Smiles.GetBitmap())
self.AssignImageList(il)
 
# now put an image on the first tab we just created:
self.SetPageImage(0, idx1)

As you can see, we first create an ImageList and set the images to be 16×16. Then I use the “images” module from the wxPython Demo to create a smiley face and add that to the list. Next I assign the list to the notebook using AssignImageList(). To set one of the images from the ImageList on one of the tabs, you call SetPageImage() and pass the tab index as the first argument and the bitmap ImageList item instance as the second (i.e. idx1). In this example, I only add an image to the first tab.

The next few lines of code adds two more tabs to the notebook and then binds a couple of events: EVT_NOTEBOOK_PAGE_CHANGING and EVT_NOTEBOOK_PAGE_CHANGED. The EVT_NOTEBOOK_PAGE_CHANGING is fired when the user has clicked on a different tab then the one currently selected and ends when the new tab is fully selected. When the next tab is fully selected is when the EVT_NOTEBOOK_PAGE_CHANGED event gets fired. One handy use for the EVT_NOTEBOOK_PAGE_CHANGING event is to veto the event should you need the user to do something before switching tabs.

The documentation is your friend. You will find many handy methods there, such as GetPage, GetPageCount, GetPageImage, GetSelection, RemovePage, and DeletePage as well as many others.

The last topic I need to touch on for the wx.Notebook is how to nest the tabs. Nesting tabs is actually very easy to do in wxPython. All you need to do is create a panel with a notebook on it and then make that panel into a tab on another Notebook. It’s kind of hard to explain, so let’s look at some code instead:

import panelOne, panelTwo
import wx
 
class TabPanel(wx.Panel):
    """
    This will be the first notebook tab
    """
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """"""
 
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
 
        # Create some nested tabs on the first tab
        nestedNotebook = wx.Notebook(self, wx.ID_ANY)
        nestedTabOne = panelOne.TabPanel(nestedNotebook)
        nestedTabTwo = panelTwo.TabPanel(nestedNotebook)
        nestedNotebook.AddPage(nestedTabOne, "NestedTabOne")
        nestedNotebook.AddPage(nestedTabTwo, "NestedTabTwo")
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(nestedNotebook, 1, wx.ALL|wx.EXPAND, 5)
 
        self.SetSizer(sizer)

The above code just creates the panel with a notebook on it. I reused some of the panels for this sub-notebook. Now we just need to insert it into another notebook (or vice-versa):

import images
import wx
import nestedPanelOne, panelTwo, panelThree
 
########################################################################
class NestedNotebookDemo(wx.Notebook):
    """
    Notebook class
    """
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style=
                             wx.BK_DEFAULT
                             #wx.BK_TOP 
                             #wx.BK_BOTTOM
                             #wx.BK_LEFT
                             #wx.BK_RIGHT
                             )
 
        # Create the first tab and add it to the notebook
        tabOne = nestedPanelOne.TabPanel(self)        
        self.AddPage(tabOne, "TabOne")
 
        # Show how to put an image on one of the notebook tabs,
        # first make the image list:
        il = wx.ImageList(16, 16)
        idx1 = il.Add(images.Smiles.GetBitmap())
        self.AssignImageList(il)
 
        # now put an image on the first tab we just created:
        self.SetPageImage(0, idx1)
 
        # Create and add the second tab
        tabTwo = panelTwo.TabPanel(self)
        self.AddPage(tabTwo, "TabTwo")
 
        # Create and add the third tab
        self.AddPage(panelThree.TabPanel(self), "TabThree")

This code snippet really isn’t very different from our very first piece of code. The only difference is importing the right panel and adding it as a page to the notebook. Now we’ll move on to our next book control.

(Click page 2 to continue)

Print Friendly