# 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()