Pairing Notebook Panel to Frame Menus
The wx.Notebook allows users to access different panels at different times. This page describes a way to link menus to those panels in such a way that the menus swap out, or can be disabled when the panel is not selected in the notebook.
To do this we will need:
- 2 wx.Panels
- 2 wx.Menus to match those panels
- 1 wx.Notebook
- 1 wx.Frame with a wx.Menubar
We want to change the panel through three methods:
- Clicking on a tab in the notebook
- Selecting a tab in a menu
- An accelerator key
The third method will be taken care of by the second. The program has to change the menu when the tab changes, and change the tab when the menu changes.
1 import wx
2
3 class NotebookPanel(wx.Panel):
4 # Subclass to get everything the normal panel can do
5 def __init__(self,parent,name,menu,menuName):
6 wx.Panel.__init__(self,parent=parent)
7 ### Attaching the menu to the panel is one way to make sure
8 ### the panel and menu stay together
9 self.Menu = menu
10 self.MenuName = menuName
11 ### All the panels will have the same information
12 ### In a real-world app, delete these lines from this class
13 ### and populate the subclasses of this class
14 sizer = wx.BoxSizer(wx.VERTICAL)
15 label = wx.StaticText(self,label="This is a sample notebook panel")
16 sizer.Add(label)
17
18 class BluePanel(NotebookPanel):
19 ### The Blue Panel has a menu
20 def __init__(self,parent):
21 blueMenu = wx.Menu()
22 blueMenu.Append(wx.ID_ANY,"This is the blue menu","Dummy Help")
23 NotebookPanel.__init__(self,
24 parent=parent,
25 name="Blue",
26 menu=blueMenu,
27 menuName="&Blue")
28 self.SetBackgroundColour("Light Blue")
29
30 class RedPanel(NotebookPanel):
31 def __init__(self,parent):
32 redMenu = wx.Menu()
33 redMenu.Append(wx.ID_ANY,"This is the red menu","Dummy Help")
34 NotebookPanel.__init__(self,
35 parent=parent,
36 name="Red",
37 menu=redMenu,
38 menuName="&Red")
39 self.SetBackgroundColour("Red")
40
41 class MyNotebook(wx.Notebook):
42 ### For this demo, we don't need to subclass
43 ### Notebook at all.
44 def __init__(self,parent):
45 wx.Notebook.__init__(self,parent=parent)
46 self.AddPage(BluePanel(self),"Blue")
47 self.AddPage(RedPanel(self),"Red")
48
49 class MyFrame(wx.Frame):
50 def __init__(self,parent):
51 wx.Frame.__init__(self,parent=parent,title="Notebook and Menu Test")
52 mbar = wx.MenuBar()
53 self.SetMenuBar(mbar)
54
55 sbar = self.CreateStatusBar()
56 self.Notebook = MyNotebook(self)
57
58 ### Create a menu that allows user to select tabs as well
59 ### This uses ID values of 0 and 1 for the menu items.
60 tabmenu = wx.Menu()
61 for p in range(self.Notebook.GetPageCount()):
62 name = self.Notebook.GetPageText(p)
63
64 tabmenu.Append(p,"%s\tCtrl+%d" % (name,p+1),"Go to the %s page" % (name))
65 self.Bind(wx.EVT_MENU,self.GoToPage,id=p)
66 tabmenu.AppendSeparator()
67 tabmenu.Append(wx.ID_CLOSE,"Close\tCtrl+X","Exit the demo")
68 self.Bind(wx.EVT_MENU,self.Goodbye,id=wx.ID_CLOSE)
69 mbar.Append(tabmenu,"&Tabs")
70
71 ### Add the current page's menu to the bar
72 m = self.Notebook.GetCurrentPage().Menu
73 n = self.Notebook.GetCurrentPage().MenuName
74
75 mbar.Append(m,n)
76
77
78 self.SetMenuBar(mbar)
79 sizer = wx.BoxSizer()
80 sizer.Add(self.Notebook,1,wx.EXPAND)
81 self.SetSizerAndFit(sizer)
82 self.Layout()
83
84 ### The EVT_NOTEBOOX_PAGE_CHANGED is registered
85 ### at the frame level
86 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED,self.AdjustMenus)
87 self.Bind(wx.EVT_MENU,self.OnMenu)
88
89 ### This disables the currently selected menu item
90 mbar.Enable(self.Notebook.GetSelection(),False)
91
92 def GoToPage(self,evt):
93 ### This is the reaction to selecting a tab from
94 ### the tab menu
95 self.Notebook.ChangeSelection(evt.GetId())
96 self.AdjustMenus(evt)
97
98 def Goodbye(self,evt):
99 self.Close(True)
100
101 def AdjustMenus(self,evt):
102 ### Menubar position 1 is the swapping menu
103
104 m = self.Notebook.GetCurrentPage().Menu
105 n = self.Notebook.GetCurrentPage().MenuName
106 mbar = self.GetMenuBar()
107 mbar.Replace(1,m,n)
108 ### Disable current page on tab menu
109 ### because the tabmenu item id's match the index
110 ### of the notebook page index, this works
111 for page in range(self.Notebook.GetPageCount()):
112 if page == self.Notebook.GetSelection():
113 ### The Menubar can enable any menu item by ID alone
114 mbar.Enable(page,False)
115 else:
116 mbar.Enable(page,True)
117 ### If you don't enable this, trying to
118 ### Change a notebook panel to the current panel
119 ### raises an error
120
121
122 def OnMenu(self,evt):
123 ### While not used in this demo, if the other menus
124 ### actually did anything, this method would catch them
125 evt.Skip()
126
127 class App(wx.App):
128 def OnInit(self):
129 frame = MyFrame(None)
130 self.SetTopWindow(frame)
131 frame.Show()
132 return 1
133
134 A = App(0)
135 A.MainLoop()
This solution seems clunky, and it is possible to control these changes using wx.lib.pubsub.Publisher. See Controlling GUI with Publisher.