1# Main menubar 2 3import string 4import types 5import tkinter 6import Pmw 7 8class MainMenuBar(Pmw.MegaArchetype): 9 10 def __init__(self, parent = None, **kw): 11 12 # Define the megawidget options. 13 INITOPT = Pmw.INITOPT 14 optiondefs = ( 15 ('balloon', None, None), 16 ('hotkeys', 1, INITOPT), 17 ('hull_tearoff', 0, None), 18 ) 19 self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu',)) 20 21 # Initialise the base class (after defining the options). 22 Pmw.MegaArchetype.__init__(self, parent, tkinter.Menu) 23 24 self._menuInfo = {} 25 self._menuInfo[None] = (None, []) 26 # Map from a menu name to a tuple of information about the menu. 27 # The first item in the tuple is the name of the parent menu (for 28 # toplevel menus this is None). The second item in the tuple is 29 # a list of status help messages for each item in the menu. 30 # The key for the information for the main menubar is None. 31 32 self._menu = self.interior() 33 self._menu.bind('<Leave>', self._resetHelpmessage) 34 self._menu.bind('<Motion>', 35 lambda event=None, self=self: self._menuHelp(event, None)) 36 37 # Check keywords and initialise options. 38 self.initialiseoptions() 39 40 def deletemenuitems(self, menuName, start, end = None): 41 self.component(menuName).delete(start, end) 42 if end is None: 43 del self._menuInfo[menuName][1][start] 44 else: 45 self._menuInfo[menuName][1][start:end+1] = [] 46 47 def deletemenu(self, menuName): 48 """Delete should be called for cascaded menus before main menus. 49 """ 50 51 parentName = self._menuInfo[menuName][0] 52 del self._menuInfo[menuName] 53 if parentName is None: 54 parentMenu = self._menu 55 else: 56 parentMenu = self.component(parentName) 57 58 menu = self.component(menuName) 59 menuId = str(menu) 60 for item in range(parentMenu.index('end') + 1): 61 if parentMenu.type(item) == 'cascade': 62 itemMenu = str(parentMenu.entrycget(item, 'menu')) 63 if itemMenu == menuId: 64 parentMenu.delete(item) 65 del self._menuInfo[parentName][1][item] 66 break 67 68 self.destroycomponent(menuName) 69 70 def disableall(self): 71 for index in range(len(self._menuInfo[None][1])): 72 self.entryconfigure(index, state = 'disabled') 73 74 def enableall(self): 75 for index in range(len(self._menuInfo[None][1])): 76 self.entryconfigure(index, state = 'normal') 77 78 def addmenu(self, menuName, balloonHelp, statusHelp = None, 79 traverseSpec = None, **kw): 80 if statusHelp is None: 81 statusHelp = balloonHelp 82 self._addmenu(None, menuName, balloonHelp, statusHelp, 83 traverseSpec, kw) 84 85 def addcascademenu(self, parentMenuName, menuName, statusHelp='', 86 traverseSpec = None, **kw): 87 self._addmenu(parentMenuName, menuName, None, statusHelp, 88 traverseSpec, kw) 89 90 def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp, 91 traverseSpec, kw): 92 93 if (menuName) in self.components(): 94 raise ValueError('menu "%s" already exists' % menuName) 95 96 menukw = {} 97 if 'tearoff' in kw: 98 menukw['tearoff'] = kw['tearoff'] 99 del kw['tearoff'] 100 else: 101 menukw['tearoff'] = 0 102 if 'name' in kw: 103 menukw['name'] = kw['name'] 104 del kw['name'] 105 106 if 'label' not in kw: 107 kw['label'] = menuName 108 109 self._addHotkeyToOptions(parentMenuName, kw, traverseSpec) 110 111 if parentMenuName is None: 112 parentMenu = self._menu 113 balloon = self['balloon'] 114 # Bug in Tk: balloon help not implemented 115 # if balloon is not None: 116 # balloon.mainmenubind(parentMenu, balloonHelp, statusHelp) 117 else: 118 parentMenu = self.component(parentMenuName) 119 120 parentMenu.add_cascade(*(), **kw) 121 122 menu = self.createcomponent(*(menuName, 123 (), 'Menu', 124 tkinter.Menu, (parentMenu,)), **menukw) 125 parentMenu.entryconfigure('end', menu = menu) 126 127 self._menuInfo[parentMenuName][1].append(statusHelp) 128 self._menuInfo[menuName] = (parentMenuName, []) 129 130 menu.bind('<Leave>', self._resetHelpmessage) 131 menu.bind('<Motion>', 132 lambda event=None, self=self, menuName=menuName: 133 self._menuHelp(event, menuName)) 134 135 def addmenuitem(self, menuName, itemType, statusHelp = '', 136 traverseSpec = None, **kw): 137 138 menu = self.component(menuName) 139 if itemType != 'separator': 140 self._addHotkeyToOptions(menuName, kw, traverseSpec) 141 142 if itemType == 'command': 143 command = menu.add_command 144 elif itemType == 'separator': 145 command = menu.add_separator 146 elif itemType == 'checkbutton': 147 command = menu.add_checkbutton 148 elif itemType == 'radiobutton': 149 command = menu.add_radiobutton 150 elif itemType == 'cascade': 151 command = menu.add_cascade 152 else: 153 raise ValueError('unknown menuitem type "%s"' % itemType) 154 155 self._menuInfo[menuName][1].append(statusHelp) 156 command(*(), **kw) 157 158 def _addHotkeyToOptions(self, menuName, kw, traverseSpec): 159 160 if (not self['hotkeys'] or 'underline' in kw or 161 'label' not in kw): 162 return 163 164 if type(traverseSpec) == int: 165 kw['underline'] = traverseSpec 166 return 167 168 if menuName is None: 169 menu = self._menu 170 else: 171 menu = self.component(menuName) 172 hotkeyList = [] 173 end = menu.index('end') 174 if end is not None: 175 for item in range(end + 1): 176 if menu.type(item) not in ('separator', 'tearoff'): 177 #Python 3 conversion 178# underline = \ 179# string.atoi(str(menu.entrycget(item, 'underline'))) 180 underline = \ 181 int(str(menu.entrycget(item, 'underline'))) 182 if underline != -1: 183 label = str(menu.entrycget(item, 'label')) 184 if underline < len(label): 185 hotkey = label[underline].lower() 186 if hotkey not in hotkeyList: 187 hotkeyList.append(hotkey) 188 189 name = kw['label'] 190 191 if type(traverseSpec) is str: 192 lowerLetter = traverseSpec.lower() 193 if traverseSpec in name and lowerLetter not in hotkeyList: 194 kw['underline'] = name.index(traverseSpec) 195 else: 196 targets = string.digits + string.ascii_letters 197 lowerName = name.lower() 198 for letter_index in range(len(name)): 199 letter = lowerName[letter_index] 200 if letter in targets and letter not in hotkeyList: 201 kw['underline'] = letter_index 202 break 203 204 def _menuHelp(self, event, menuName): 205 if menuName is None: 206 menu = self._menu 207 index = menu.index('@%d'% event.x) 208 else: 209 menu = self.component(menuName) 210 index = menu.index('@%d'% event.y) 211 212 balloon = self['balloon'] 213 if balloon is not None: 214 if index is None: 215 balloon.showstatus('') 216 else: 217 if str(menu.cget('tearoff')) == '1': 218 index = index - 1 219 if index >= 0: 220 help = self._menuInfo[menuName][1][index] 221 balloon.showstatus(help) 222 223 def _resetHelpmessage(self, event=None): 224 balloon = self['balloon'] 225 if balloon is not None: 226 balloon.clearstatus() 227 228Pmw.forwardmethods(MainMenuBar, tkinter.Menu, '_hull') 229