#!/usr/bin/env python ###################################################################### # Provide classes to simplify creating menubars, pulldown menus, etc. # # Mitch Chapman #--------------------------------------------------------------------- # $Log: Menu.py,v $ # Revision 1.1 1996/12/01 22:58:54 mchapman # Initial revision # ###################################################################### __version__ = "$Revision: 1.1 $" import Tkinter; Tk=Tkinter import KWDict, string ###################################################################### # Convert a string of the form "&Something" to a tuple: # ("Something", 0) # indicating that the first letter of "Something" should be # underlined. If no "&" occurs inside the string, the second tuple # item will be None. ###################################################################### def findUnderline(title): ul = None i = string.find(title, "&") if i >= 0: ul = i title = title[:ul] + title[ul+1:] return title, ul ###################################################################### # This class represents a menubar, w. default relief, etc. ###################################################################### class Menubar(Tk.Frame): ################################################################## # Create a new instance. ################################################################## def __init__(self, master=None, **kw): opts = {'relief':'raised', 'bd':2} for k,v in kw.items(): opts[k] = v apply(Tk.Frame.__init__, (self, master), opts) Tk.Pack.config(self, side='top', fill='x') ###################################################################### # This class represents a menu item in a Tk menu. ###################################################################### class Item: ################################################################## # Create a new instance. # master is the pane to which to append the new item. # title is the text which will appear inside the menu item. It # also determines what sort of menu item will be created. See # below for details. # cmd is the optional callback for this menu item. # items specifies any items to be displayed in cascaded menus. ################################################################## def __init__(self, master, title, cmd=None, *items): self.master = master self.title = title if title[0] == "-": master.add_separator() self.index = master.index('last') return submenu = None createFn = master.add_command prefix = title[0:2] if prefix == "c_": createFn = master.add_checkbutton elif prefix == "r_": createFn = master.add_radiobutton elif prefix == "m_": createFn = master.add_cascade submenu = Pane(master) apply(submenu.addItems, items) else: title = "x_" + title title, ul = findUnderline(title[2:]) # XXX no means is provided to associate a Tk variable with a # radio button or a check button. apply(createFn, (), KWDict.dict(label=title, command=cmd, underline=ul, menu=submenu)) self.index = master.index('last') ################################################################## # Enable or disable an item. ################################################################## def enable(self, doEnable=1): if doEnable: newstate = 'normal' else: newstate = 'disabled' try: # NOTE: This won't work for tear-off entries, or for separators. self.master.tk.call(self.master._w, 'entryconfigure', self.index, '-state', newstate) except Tk.TclError: pass ###################################################################### # This class represents a pulldown menu. ###################################################################### class Pulldown: ################################################################## # Create a new Pulldown menu. # Each additional item should be a list consisting of menu # item string and optional callback. Menu item strings are # interpreted as indicated in parseMenuStr(). # Returns the menu button which posts the new menu. ################################################################## def __init__(self, master, title, *items, **kw): self.master = master packside='left' if kw and 'side' in kw.keys(): packside=kw['side'] # Create the menu button. title, ul = findUnderline(title) btnKeywords = KWDict.dict(text=title, underline=ul) btn = apply(Tk.Menubutton, (master,), btnKeywords) # Create the menu pane and associate it with the button. menu = btn.menu = Pane(btn) btn['menu'] = menu apply(menu.addItems, items) btn.pack(side=packside, padx='2m') ###################################################################### # This class extends the Tkinter Menu class with convenience methods # for enabling/disabling menu items and for support of popup menus. ###################################################################### class Pane(Tk.Menu): ################################################################## # Init a new instance. ################################################################## def __init__(self, master=None, **kw): apply(Tk.Menu.__init__, (self, master), kw) self.items = [] ################################################################## # Add items to a menu Pane. ################################################################## def addItems(self, *newItems): # Create a menu item for each described item. for item in newItems: self.items.append(apply(Item, (self,) + tuple(item))) ################################################################## # Enable or disable a menu item. # If enable is non-zero, then the menu item is enabled; otherwise # it is disabled. ################################################################## def enable(self, index, enable=1): self.items[index].enable(enable) ################################################################## # Pop up a menu pane -- # useful when Pane is being used as a popup menu. ################################################################## def tk_popup(self, event=None): x, y = 0, 0 if event: x, y = event.x_root, event.y_root self.tk.call('tk_popup', self._w, x, y) ###################################################################### # Mainline for testing ###################################################################### def main(): import TkDlgWrapper; TkDlg=TkDlgWrapper # To demonstrate, here's a simple file editor. f = Tk.Frame() m = Menubar(f) t = Tk.Text(f) # Here are the file types which the open/save dialogs will recognize. filetypes = (("Python", ".py"), ("Text", ".txt"), ("All Files", "*")) openFile = TkDlg.OpenFile(f, defaultextension=".py", filetypes=filetypes) saveFile = TkDlg.SaveFile(f, defaultextension=".py", filetypes=filetypes) # Here's an example of a popup menu. It should appear when # you click mouse button 3 inside the text area. textPopup = Pane(f, tearoff='0') textPopup.addItems(["Cut"], ["Copy"], ["Paste"], ["Clear"]) # Initially, disable all of the items. Enable them when a file # is actually opened. for item in textPopup.items: item.enable(0) t.bind("<3>", textPopup.tk_popup) def openAFile(openDlg = openFile, text=t, popup=textPopup): filename = openDlg.show() if filename: text.delete('1.0', 'end') inf = open(filename) text.insert('end', inf.read()) inf.close() text.winfo_toplevel().title("Menu Example:%s" % filename) # Enable the popup menu items. for item in popup.items: item.enable(1) def saveAFile(saveDlg = saveFile, text=t): filename = saveDlg.show() if filename: outf = open(filename, "w") outf.write(text.get('1.0', 'end')) outf.close() text.winfo_toplevel().title("Menu Example:%s" % filename) # Keyboard menu traversal appears to be broken under Tk 4.2 Try using # keyboard traversal in the tk4.2 library's demo subdirectory... fileMenu = Pulldown(m, "&File", ["&Open...", openAFile], ["Save &As...", saveAFile], ["----"], ["E&xit", m.quit]) helpMenu = Pulldown(m, "&Help", ["&About"], side='right') m.pack() t.pack() f.pack() f.master.title("Menu Example") f.mainloop() if __name__ == "__main__": main()