# Manager widget for menus.
import string
import types
import Tkinter
import Pmw
def _findHotkey(hotkeys, name, traverseSpec):
lowerName = string.lower(name)
if traverseSpec is not None:
if type(traverseSpec) == types.StringType:
lowerLetter = string.lower(traverseSpec)
if traverseSpec in name and lowerLetter not in hotkeys:
hotkeys.append(lowerLetter)
return string.index(name, traverseSpec), traverseSpec
elif type(traverseSpec) == types.IntType:
if traverseSpec < len(name):
hotkeys.append(lowerName[traverseSpec])
return traverseSpec, lowerName[traverseSpec]
for letter_index in range(len(name)):
letter = lowerName[letter_index]
if letter in (string.digits + string.letters) and letter not in hotkeys:
hotkeys.append(letter)
return letter_index, letter
return None, None
class MenuBar(Pmw.MegaWidget):
def __init__(self, parent = None, **kw):
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('balloon', None, None),
('padx', 0, INITOPT),
('hotkeys', 1, INITOPT),
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, parent)
# Initialise instance variables.
self._menuHelpDict = {}
# Map from a menu name to a tuple of information about the
# menu's hotkeys. The first item in the tuple is a list of
# hotkeys of the items in the menu. The second item is the
# parent menu (required because of cascaded menus). The third
# item is the hotkey of this menu in it parent. The last two
# are used when menus are deleted. Includes information for
# the toplevel menubuttons as item None.
self._menuHotkeys = {None : ([], None, None)}
# Check keywords and initialise options.
self.initialiseoptions(MenuBar)
def deletemenuitems(self, menuName, start='0', end=None):
if (menuName + '-menu') in self.components():
self.component(menuName + '-menu').delete(start, end)
if self._menuHelpDict.has_key(menuName + '-menu'):
if end is None:
del self._menuHelpDict[menuName + '-menu'][start]
else:
self._menuHelpDict[menuName + '-menu'][start:end+1] = []
def deletemenu(self, menuName):
"""Delete should be called for cascaded menus before main menus.
"""
if (menuName + '-menu') in self.components():
self.destroycomponent(menuName + '-menu')
if (menuName + '-button') in self.components():
self.destroycomponent(menuName + '-button')
if self._menuHelpDict.has_key(menuName + '-menu'):
del self._menuHelpDict[menuName + '-menu']
if self._menuHotkeys.has_key(menuName):
parent = self._menuHotkeys[menuName][1]
hotkey = self._menuHotkeys[menuName][2]
hotkeyList = self._menuHotkeys[parent][0]
if hotkey in hotkeyList:
hotkeyList.remove(hotkey)
del self._menuHotkeys[menuName]
def disableall(self):
for item in self.components():
if len(item) > 6 and item[-6:] == 'button':
self.component(item).configure(state='disabled')
def enableall(self):
for item in self.components():
if len(item) > 6 and item[-6:] == 'button':
self.component(item).configure(state='normal')
def addcascademenu(self, menuName, submenu, help='',
traverseSpec=None, **kw):
if (submenu + '-menu') in self.components():
raise ValueError, 'submenu "%s" already exists' % submenu
parentmenu_w = self.component(menuName + '-menu')
submenu_w = self.createcomponent(submenu + '-menu',
(), 'Menu',
Tkinter.Menu,(parentmenu_w,), tearoff=0)
self._menuHelpDict[submenu] = []
kw['menu'] = submenu_w
if not kw.has_key('label'):
kw['label'] = submenu
if self['hotkeys']:
hotkey = None
if not kw.has_key('underline'):
hotkeyList = self._menuHotkeys[menuName][0]
underline, hotkey = \
_findHotkey(hotkeyList, kw['label'], traverseSpec)
if underline is not None:
kw['underline'] = underline
self._menuHotkeys[submenu] = ([], menuName, hotkey)
self._menuHelpDict[menuName].append(help)
apply(parentmenu_w.add_cascade, (), kw)
# Need to put this binding after the class bindings so that
# submenu_w.index() does not lag behind.
_bindtag = 'PmwMenuBar' + str(self) + submenu
self.bind_class(_bindtag, '<Motion>',
lambda event=None, self=self, menuName=submenu:
self._menuHelp(menuName))
submenu_w.bindtags(submenu_w.bindtags() + (_bindtag,))
submenu_w.bind('<Leave>', self._resetHelpmessage)
def addmenu(self, menuName, balloonHelp, statusHelp=None,
side='left', traverseSpec=None, **kw):
if (menuName + '-button') in self.components():
raise ValueError, 'menu "%s" already exists' % menuName
if not kw.has_key('text'):
kw['text'] = menuName
if self['hotkeys']:
hotkey = None
if not kw.has_key('underline'):
hotkeyList = self._menuHotkeys[None][0]
underline, hotkey = \
_findHotkey(hotkeyList, kw['text'], traverseSpec)
if underline is not None:
kw['underline'] = underline
self._menuHotkeys[menuName] = ([], None, hotkey)
button = apply(self.createcomponent, (menuName + '-button',
(), 'Button',
Tkinter.Menubutton, (self.interior(),)), kw)
button.pack(side=side, padx = self['padx'])
balloon = self['balloon']
if balloon is not None:
balloon.bind(button, balloonHelp, statusHelp)
menu = self.createcomponent(menuName + '-menu',
(), 'Menu',
Tkinter.Menu, (button,), tearoff=0)
button.configure(menu = menu)
self._menuHelpDict[menuName] = []
# Need to put this binding after the class bindings so that
# menu.index() does not lag behind.
_bindtag = 'PmwMenuBar' + str(self) + menuName
self.bind_class(_bindtag, '<Motion>',
lambda event=None, self=self, menuName=menuName:
self._menuHelp(menuName))
menu.bindtags(menu.bindtags() + (_bindtag,))
menu.bind('<Leave>', self._resetHelpmessage)
return button
def addmenuitem(self, menuName, type, help='', traverseSpec=None, **kw):
menu = self.component(menuName + '-menu')
if (self['hotkeys'] and type != 'separator' and
not kw.has_key('underline') and kw.has_key('label')):
hotkeyList = self._menuHotkeys[menuName][0]
underline, hotkey = \
_findHotkey(hotkeyList, kw['label'], traverseSpec)
if underline is not None:
kw['underline'] = underline
if type == 'command':
command = menu.add_command
elif type == 'separator':
command = menu.add_separator
elif type == 'checkbutton':
command = menu.add_checkbutton
elif type == 'radiobutton':
command = menu.add_radiobutton
elif type == 'cascade':
command = menu.add_cascade
else:
raise ValueError, 'unknown menuitem type "%s"' % type
self._menuHelpDict[menuName].append(help)
apply(command, (), kw)
def _menuHelp(self, menuName):
menu = self.component(menuName + '-menu')
index = menu.index('active')
if index is None:
self._resetHelpmessage()
else:
balloon = self['balloon']
if balloon is not None:
help = self._menuHelpDict[menuName][index]
balloon.showstatus(help)
def _resetHelpmessage(self, event=None):
balloon = self['balloon']
if balloon is not None:
balloon.clearstatus()