For my next demo, I created a way to add and delete pages from the FlatNotebook. Let’s see how:

flatnotebookPageDemo

Listing 3

import panelOne, panelTwo, panelThree
import random
import wx
import wx.lib.agw.flatnotebook as fnb
 
########################################################################
class FlatNotebookDemo(fnb.FlatNotebook):
    """
    Flatnotebook class
    """
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        fnb.FlatNotebook.__init__(self, parent, wx.ID_ANY)
 
########################################################################
class DemoFrame(wx.Frame):
    """
    Frame that holds all other widgets
    """
 
    #----------------------------------------------------------------------
    def __init__(self, title="FlatNotebook Add/Remove Page Tutorial"):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          title=title,
                          size=(600,400)
                          )
        self._newPageCounter = 0
        panel = wx.Panel(self)
        self.createRightClickMenu()
 
        # create some widgets
        self.notebook = FlatNotebookDemo(panel)
        addPageBtn = wx.Button(panel, label="Add Page")
        addPageBtn.Bind(wx.EVT_BUTTON, self.onAddPage)
        removePageBtn = wx.Button(panel, label="Remove Page")
        removePageBtn.Bind(wx.EVT_BUTTON, self.onDeletePage)
        self.notebook.SetRightClickMenu(self._rmenu)
 
        # create some sizers
        sizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
 
        # layout the widgets
        sizer.Add(self.notebook, 1, wx.ALL|wx.EXPAND, 5)
        btnSizer.Add(addPageBtn, 0, wx.ALL, 5)
        btnSizer.Add(removePageBtn, 0, wx.ALL, 5)
        sizer.Add(btnSizer)
        panel.SetSizer(sizer)
        self.Layout()
 
        self.Show()
 
    #----------------------------------------------------------------------
    def createRightClickMenu(self):
        """
        Based on method from flatnotebook demo
        """
        self._rmenu = wx.Menu()
        item = wx.MenuItem(self._rmenu, wx.ID_ANY,
                           "Close Tab\tCtrl+F4",
                           "Close Tab")
        self.Bind(wx.EVT_MENU, self.onDeletePage, item)
        self._rmenu.AppendItem(item)
 
    #----------------------------------------------------------------------
    def onAddPage(self, event):
        """
        This method is based on the flatnotebook demo
 
        It adds a new page to the notebook
        """
        caption = "New Page Added #" + str(self._newPageCounter)
        self.Freeze()
 
        self.notebook.AddPage(self.createPage(caption), caption, True)
        self.Thaw()
        self._newPageCounter = self._newPageCounter + 1
 
    #----------------------------------------------------------------------
    def createPage(self, caption):
        """
        Creates a notebook page from one of three
        panels at random and returns the new page
        """
        panel_list = [panelOne, panelTwo, panelThree]
        obj = random.choice(panel_list)
        page = obj.TabPanel(self.notebook)
        return page
 
    #----------------------------------------------------------------------
    def onDeletePage(self, event):
        """
        This method is based on the flatnotebook demo
 
        It removes a page from the notebook
        """
        self.notebook.DeletePage(self.notebook.GetSelection())
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = DemoFrame()
    app.MainLoop()

The code above allows the user to add as many pages as they want by clicking the Add Page button. The Remove Page button will remove whatever page is currently selected. When adding a page, but button handler freezes the frame and calls the notebook’s AddPage method. This calls the “createPage” method which randomly grabs one of my pre-defined panels, instantiates it and returns it to the AddPage method. On returning to the “onAddPage” method, the frame is thawed and the page counter is incremented.

The Remove Page button calls the notebook’s GetSelection() method to get the currently selected tab and then calls the notebook’s DeletePage() method to remove it from the notebook.

Another fun functionality that I enabled was the tab right-click menu, which gives us another way to close a tab, although you could use to do other actions as well. All you need to do to enable it is to call the notebook’s SetRightClickMenu() method and pass in a wx.Menu object.

There are tons of other features for you to explore as well. Be sure to check out the FlatNotebook demo in the official wxPython demo where you can learn to close tabs with the middle mouse button or via double-clicks, turn on gradient colors for the tab background, disable tabs, enable smart tabbing (which is kind of like the alt+tab menu in Windows), create drag-and-drop tabs between notebooks and much, much more!

AGW AUI Notebook

agwAuiNotebookDemo

Andrea Gavana went to the trouble of creating a pure python version of the Advanced User Interface (AUI) that provides perspective saving, floating sub-windows that can be docked, customizable look and feel and the splittable AUI Notebook. His notebook will be the focus of this section. The AGW AUI Notebook has lots of features, but I’m just going to go over some of the basics. If you want to see all the features, be sure to read the code and check out the demo in the official wxPython Demo. As I mentioned at the beginning of this tutorial, be sure to download the latest version of AUI (or AGW as a whole) from SVN to get all the bug fixes.

Let’s take a look at the simple example I used for the screenshot above:

Listing 4

#----------------------------------------------------------------------
# agwAUINotebook.py
#
# Created: December 2009
#
# Author: Mike Driscoll - mike@pythonlibrary.org
#
# Note: Some code comes from the wxPython demo
#
#----------------------------------------------------------------------
 
 
import wx
import wx.lib.agw.aui as aui
 
########################################################################
class TabPanelOne(wx.Panel):
    """
    A simple wx.Panel class
    """
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """"""
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
 
        izer = 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)
 
########################################################################
class DemoFrame(wx.Frame):
    """
    wx.Frame class
    """
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          "AGW AUI Notebook Tutorial",
                          size=(600,400))
 
        self._mgr = aui.AuiManager()
 
        # tell AuiManager to manage this frame
        self._mgr.SetManagedWindow(self)
 
        notebook = aui.AuiNotebook(self)
        panelOne = TabPanelOne(notebook)
        panelTwo = TabPanelOne(notebook)
 
        notebook.AddPage(panelOne, "PanelOne", False)
        notebook.AddPage(panelTwo, "PanelTwo", False)
 
        self._mgr.AddPane(notebook,
                          aui.AuiPaneInfo().Name("notebook_content").
                          CenterPane().PaneBorder(False))
        self._mgr.Update()
        #notebook.EnableTab(1, False)
 
 #----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = DemoFrame()
    frame.Show()
    app.MainLoop()

The first difference between this notebook and the original AuiNotebook is that this one requires an AuiManager object. It may be that something similar is behind the original as well, but that’s hidden from us. Anyway, the first step is instantiating the AuiManager and then giving it the frame to manage via its SetManagedWindow() method. Now we can add the AUI Notebook. Note that we pass the frame as the parent of the notebook instead of the AuiManager. I think the reason is that when the AuiManager is given the frame, it becomes the top level window.

The next part of the equation should look familiar: AddPage(). Let’s see what it accepts:

AddPage(self, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap, control=None)

In my code, I only pass in the first three parameters, but you can also add a couple bitmaps and a wx.Window for the control. The next bit is a little tricky. We need to call the AuiManager’s AddPane() method to tell the AuiManager that we want it to “manage” something (in this case, the notebook). We also pass in a second argument which looks kind of confusing:

aui.AuiPaneInfo().Name("notebook_content").CenterPane().PaneBorder(False))

This parameter tells the AuiManager what to do with the notebook. In this case, we are telling it that the pane’s (i.e the notebook’s) name is “notebook_content”, which is what we use to look up the pane. We’re also telling the AuiManager that we want the pane to be in the centered dock position and the PaneBorder(False) command tells the AuiManager that we want a hidden border drawn around the notebook pane.