wx.Notebook

The wx.Notebook is a control that many people will be familiar with if they've used Windows for very long or Mozilla Firefox. Basically, a notebook is a widget that contains one or more tabs that you can switch between. On Windows, you usually see them in preference dialogs. The following is a screenshot of what a notebook could look like:

http://www.blog.pythonlibrary.org/wp-content/uploads/2009/12/notebookDemo.png

Now let's create this notebook so we can learn how it's done. Each notebook tab (or page) will be made with a wx.Panel object. I have taken some code from the wxPython Demo for these panels (and the book widgets) and modified them for this tutorial. Here's a runnable example that will generate the example shown in the screenshot above:

   1 import images
   2 import wx
   3 
   4 ########################################################################
   5 class TabPanel(wx.Panel):
   6     """
   7     This will be the first notebook tab
   8     """
   9     #----------------------------------------------------------------------
  10     def __init__(self, parent):
  11         """"""
  12  
  13         wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
  14  
  15         sizer = wx.BoxSizer(wx.VERTICAL)
  16         txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
  17         txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
  18  
  19         sizer = wx.BoxSizer(wx.VERTICAL)
  20         sizer.Add(txtOne, 0, wx.ALL, 5)
  21         sizer.Add(txtTwo, 0, wx.ALL, 5)
  22  
  23         self.SetSizer(sizer)
  24 
  25  
  26 ########################################################################
  27 class NotebookDemo(wx.Notebook):
  28     """
  29     Notebook class
  30     """
  31  
  32     #----------------------------------------------------------------------
  33     def __init__(self, parent):
  34         wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style=
  35                              wx.BK_DEFAULT
  36                              #wx.BK_TOP 
  37                              #wx.BK_BOTTOM
  38                              #wx.BK_LEFT
  39                              #wx.BK_RIGHT
  40                              )
  41  
  42         # Create the first tab and add it to the notebook
  43         tabOne = TabPanel(self)
  44         tabOne.SetBackgroundColour("Gray")
  45         self.AddPage(tabOne, "TabOne")
  46  
  47         # Show how to put an image on one of the notebook tabs,
  48         # first make the image list:
  49         il = wx.ImageList(16, 16)
  50         idx1 = il.Add(images.Smiles.GetBitmap())
  51         self.AssignImageList(il)
  52  
  53         # now put an image on the first tab we just created:
  54         self.SetPageImage(0, idx1)
  55  
  56         # Create and add the second tab
  57         tabTwo = TabPanel(self)
  58         self.AddPage(tabTwo, "TabTwo")
  59  
  60         # Create and add the third tab
  61         self.AddPage(TabPanel(self), "TabThree")
  62  
  63         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
  64         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
  65  
  66     def OnPageChanged(self, event):
  67         old = event.GetOldSelection()
  68         new = event.GetSelection()
  69         sel = self.GetSelection()
  70         print 'OnPageChanged,  old:%d, new:%d, sel:%d\n' % (old, new, sel)
  71         event.Skip()
  72  
  73     def OnPageChanging(self, event):
  74         old = event.GetOldSelection()
  75         new = event.GetSelection()
  76         sel = self.GetSelection()
  77         print 'OnPageChanging, old:%d, new:%d, sel:%d\n' % (old, new, sel)
  78         event.Skip()
  79  
  80  
  81 ########################################################################
  82 class DemoFrame(wx.Frame):
  83     """
  84     Frame that holds all other widgets
  85     """
  86  
  87     #----------------------------------------------------------------------
  88     def __init__(self):
  89         """Constructor"""
  90         wx.Frame.__init__(self, None, wx.ID_ANY,
  91                           "Notebook Tutorial",
  92                           size=(600,400)
  93                           )
  94         panel = wx.Panel(self)
  95  
  96         notebook = NotebookDemo(panel)
  97         sizer = wx.BoxSizer(wx.VERTICAL)
  98         sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
  99         panel.SetSizer(sizer)
 100         self.Layout()
 101  
 102         self.Show()
 103  
 104 #----------------------------------------------------------------------
 105 if __name__ == "__main__":
 106     app = wx.PySimpleApp()
 107     frame = DemoFrame()
 108     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:

   1 # Create the first tab and add it to the notebook
   2 tabOne = TabPanel(self)
   3 tabOne.SetBackgroundColour("Gray")
   4 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:

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

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

   1 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:

   1 # Show how to put an image on one of the notebook tabs,
   2 # first make the image list:
   3 il = wx.ImageList(16, 16)
   4 idx1 = il.Add(images.Smiles.GetBitmap())
   5 self.AssignImageList(il)
   6  
   7 # now put an image on the first tab we just created:
   8 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.

Nesting Notebooks

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:

   1 import images
   2 import wx
   3 
   4 class PanelOne(wx.Panel):
   5     """
   6     This will be the first notebook tab
   7     """
   8     #----------------------------------------------------------------------
   9     def __init__(self, parent):
  10         """"""
  11         
  12         wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
  13         
  14         sizer = wx.BoxSizer(wx.VERTICAL)
  15         txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
  16         txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
  17         
  18         sizer = wx.BoxSizer(wx.VERTICAL)
  19         sizer.Add(txtOne, 0, wx.ALL, 5)
  20         sizer.Add(txtTwo, 0, wx.ALL, 5)
  21         
  22         self.SetSizer(sizer)
  23 
  24 class NestedPanel(wx.Panel):
  25     """
  26     This will be the first notebook tab
  27     """
  28     #----------------------------------------------------------------------
  29     def __init__(self, parent):
  30         """"""
  31         
  32         wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
  33         
  34         sizer = wx.BoxSizer(wx.VERTICAL)
  35         
  36         # Create some nested tabs on the first tab
  37         nestedNotebook = wx.Notebook(self, wx.ID_ANY)
  38         nestedTabOne = PanelOne(nestedNotebook)
  39         nestedTabTwo = PanelOne(nestedNotebook)
  40         nestedNotebook.AddPage(nestedTabOne, "NestedTabOne")
  41         nestedNotebook.AddPage(nestedTabTwo, "NestedTabTwo")
  42         
  43         sizer = wx.BoxSizer(wx.VERTICAL)
  44         sizer.Add(nestedNotebook, 1, wx.ALL|wx.EXPAND, 5)
  45                 
  46         self.SetSizer(sizer)
  47 
  48 
  49 ########################################################################
  50 class NestedNotebookDemo(wx.Notebook):
  51     """
  52     Notebook class
  53     """
  54     
  55     #----------------------------------------------------------------------
  56     def __init__(self, parent):
  57         wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style=
  58                              wx.BK_DEFAULT
  59                              #wx.BK_TOP 
  60                              #wx.BK_BOTTOM
  61                              #wx.BK_LEFT
  62                              #wx.BK_RIGHT
  63                              )
  64         
  65         # Create the first tab and add it to the notebook
  66         tabOne = NestedPanel(self)
  67         self.AddPage(tabOne, "TabOne")
  68         
  69         # Show how to put an image on one of the notebook tabs,
  70         # first make the image list:
  71         il = wx.ImageList(16, 16)
  72         idx1 = il.Add(images.Smiles.GetBitmap())
  73         self.AssignImageList(il)
  74         
  75         # now put an image on the first tab we just created:
  76         self.SetPageImage(0, idx1)
  77         
  78         # Create and add the second tab
  79         tabTwo = PanelOne(self)
  80         self.AddPage(tabTwo, "TabTwo")
  81         
  82         # Create and add the third tab
  83         self.AddPage(PanelOne(self), "TabThree")
  84         
  85         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
  86         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
  87         
  88     def OnPageChanged(self, event):
  89         old = event.GetOldSelection()
  90         new = event.GetSelection()
  91         sel = self.GetSelection()
  92         print 'OnPageChanged,  old:%d, new:%d, sel:%d\n' % (old, new, sel)
  93         event.Skip()
  94 
  95     def OnPageChanging(self, event):
  96         old = event.GetOldSelection()
  97         new = event.GetSelection()
  98         sel = self.GetSelection()
  99         print 'OnPageChanging, old:%d, new:%d, sel:%d\n' % (old, new, sel)
 100         event.Skip()
 101 
 102         
 103 ########################################################################
 104 class DemoFrame(wx.Frame):
 105     """
 106     Frame that holds all other widgets
 107     """
 108 
 109     #----------------------------------------------------------------------
 110     def __init__(self):
 111         """Constructor"""        
 112         wx.Frame.__init__(self, None, wx.ID_ANY, 
 113                           "Notebook Tutorial",
 114                           size=(600,400)
 115                           )
 116         panel = wx.Panel(self)
 117         
 118         notebook = NestedNotebookDemo(panel)
 119         sizer = wx.BoxSizer(wx.VERTICAL)
 120         sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
 121         panel.SetSizer(sizer)
 122         self.Layout()
 123         
 124         self.Show()
 125         
 126 #----------------------------------------------------------------------
 127 if __name__ == "__main__":
 128     app = wx.PySimpleApp()
 129     frame = DemoFrame()
 130     app.MainLoop()

The basic gist of the code above is that you take a panel, but a notebook on it and then place that panel on the page of another notebook, thus getting the nested notebook.

All code was tested on the following:

For Further Information

Note: The tutorial on this page is a modified version of the tutorial on my blog

Notebooks (last edited 2009-12-13 19:04:33 by 208)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.