Thu 3 Dec 2009
The “Book” Controls of wxPython (Part 1 of 2)
Posted by Mike under Python, wxPython
View Comments
wx.Toolbook
The Toolbook is a wx.Toolbar plus a wx.Notebook, which means that you use labeled bitmap buttons to control which “tab” of the notebook you are viewing. As you probably noticed with the Listbook example, I used the wxPython demo’s “images” module for its images and I use it again in my sample code here.
import images import wx import panelOne, panelTwo, panelThree def getNextImageID(count): imID = 0 while True: yield imID imID += 1 if imID == count: imID = 0 ######################################################################## class ToolbookDemo(wx.Toolbook): """ Toolbook class """ #---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" wx.Toolbook.__init__(self, parent, wx.ID_ANY, style= wx.BK_DEFAULT #wx.BK_TOP #wx.BK_BOTTOM #wx.BK_LEFT #wx.BK_RIGHT ) # make an image list using the LBXX images il = wx.ImageList(32, 32) for x in range(3): obj = getattr(images, 'LB%02d' % (x+1)) bmp = obj.GetBitmap() il.Add(bmp) self.AssignImageList(il) imageIdGenerator = getNextImageID(il.GetImageCount()) pages = [(panelOne.TabPanel(self), "Panel One"), (panelTwo.TabPanel(self), "Panel Two"), (panelThree.TabPanel(self), "Panel Three")] imID = 0 for page, label in pages: self.AddPage(page, label, imageId=imageIdGenerator.next()) imID += 1 self.Bind(wx.EVT_TOOLBOOK_PAGE_CHANGED, self.OnPageChanged) self.Bind(wx.EVT_TOOLBOOK_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, "Toolbook Tutorial", size=(700,400) ) panel = wx.Panel(self) notebook = ToolbookDemo(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()
The Toolbook follows in the footsteps of the previous controls as its lineage comes from the wx.BookCtrlBase. This widget’s claim to fame is its look and feel (although it looks very similar to the Listbook) and the Toolbook’s unique events. I used some fun code from the wxPython demo to help in assigning images to the toolbar’s buttons, but that’s really the only difference of any importance. For full disclosure, read the docs for this control!
wx.Treebook
The Treebook control is a combination of the wx.TreeCtrl and the wx.Notebook. Most TreeCtrls that I’ve seen do not use images, but the wxPython demo uses them for the Treebook, so I decided to use them too. As you can see from the screenshot above, the bitmaps give the Treebook an interesting flavor. Let’s see how to make one of these controls!
import images import wx import panelOne, panelTwo, panelThree def getNextImageID(count): imID = 0 while True: yield imID imID += 1 if imID == count: imID = 0 ######################################################################## class TreebookDemo(wx.Treebook): """ Treebook class """ #---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" wx.Treebook.__init__(self, parent, wx.ID_ANY, style= wx.BK_DEFAULT #wx.BK_TOP #wx.BK_BOTTOM #wx.BK_LEFT #wx.BK_RIGHT ) il = wx.ImageList(32, 32) for x in range(6): obj = getattr(images, 'LB%02d' % (x+1)) bmp = obj.GetBitmap() il.Add(bmp) self.AssignImageList(il) imageIdGenerator = getNextImageID(il.GetImageCount()) pages = [(panelOne.TabPanel(self), "Panel One"), (panelTwo.TabPanel(self), "Panel Two"), (panelThree.TabPanel(self), "Panel Three")] imID = 0 for page, label in pages: self.AddPage(page, label, imageId=imageIdGenerator.next()) imID += 1 self.AddSubPage(page, 'a sub-page', imageId=imageIdGenerator.next()) self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGED, self.OnPageChanged) self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGING, self.OnPageChanging) # This is a workaround for a sizing bug on Mac... wx.FutureCall(100, self.AdjustSize) #---------------------------------------------------------------------- def AdjustSize(self): #print self.GetTreeCtrl().GetBestSize() self.GetTreeCtrl().InvalidateBestSize() self.SendSizeEvent() #---------------------------------------------------------------------- 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, "Treebook Tutorial", size=(700,400) ) panel = wx.Panel(self) notebook = TreebookDemo(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()
As with the Toolbook, I once again used a some code from the wxPython demo to create this demo and reused some of my own “framework” code. Besides the specialized events of the Treebook, you should also take note that it has an AddSubPage method, which adds a sub-node to the tree which in turn adds another page to the notebook. There are several other methods that only this control has: CollapseNode, ExpandNode, GetPageParent, GetTreeCtrl, InsertSubPage and IsNodeExpanded. I think they’re pretty self-explanatory, but don’t be afraid to read the documentation.
wx.AuiNotebook
Our final control for this article comes from the wx.aui namespace: the AuiNotebook. This is the only notebook control in this article that does not inherit from the generic BookCtrlBase. Be sure to study this control’s API to fully understand how to work with it as the AuiNotebook has several methods that are unlike the other controls we’ve seen so far.
The AuiNotebook allows the user to rearrange the tabs, drag the tab into its own area (as you can see from the screenshot above) or even drag the tab from one AuiNotebook to another. The following code snippet will allow you to do all these things:
import wx import wx.aui import panelOne, panelTwo, panelThree class DemoPanel(wx.Panel): """ This will be the first notebook tab """ #---------------------------------------------------------------------- def __init__(self, parent): """""" wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) # create the AuiNotebook instance nb = wx.aui.AuiNotebook(self) # add some pages to the notebook pages = [(panelOne.TabPanel(nb), "Tab 1"), (panelTwo.TabPanel(nb), "Tab 2"), (panelThree.TabPanel(nb), "Tab 3")] for page, label in pages: nb.AddPage(page, label) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(nb, 1, wx.EXPAND) 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, "AUI-Notebook Tutorial", size=(600,400)) panel = DemoPanel(self) self.Show() #---------------------------------------------------------------------- if __name__ == "__main__": app = wx.PySimpleApp() frame = DemoFrame() app.MainLoop()
For this simple example, I just created an instance of AuiNotebook by instantiating against wx.aui.AuiNotebook(self) and passing in a wx.Panel. Then I added some pages using the AuiNotebook’s AddPage() method. Notice also that you can close any tab by clicking on its individual “x”. Now you know how to make your own AuiNotebook!
Wrapping Up
In the second part of this article, we will discover the many interesting features of the AGW Notebooks. One of the main reasons I left them out of this round-up is because they are pure python modules and have lots of extra capabilities that are not in this set of widgets. All of this code was tested using wxPython 2.8.10.1 (unicode), Python 2.5 on Windows XP and Vista. All the examples should run fine on Mac and Linux too, but feel free to let me know how well they work in the comments!
Further Reading
Downloads
-
MrC
-
driscollis
-
MrC
-
wxLover
-
driscollis
-
wxLover
-
driscollis
-
MrC
-
driscollis
-
MrC
-
MrC
-
driscollis
-
mld
-
mld
-
RL Frost
-
Mo
-
Steven Sproat


