| 1 | import types |
| 2 | import Tkinter |
| 3 | import Pmw |
| 4 | |
| 5 | class OptionMenu(Pmw.MegaWidget): |
| 6 | |
| 7 | def __init__(self, parent = None, **kw): |
| 8 | |
| 9 | # Define the megawidget options. |
| 10 | INITOPT = Pmw.INITOPT |
| 11 | optiondefs = ( |
| 12 | ('command', None, None), |
| 13 | ('items', (), INITOPT), |
| 14 | ('initialitem', None, INITOPT), |
| 15 | ('labelmargin', 0, INITOPT), |
| 16 | ('labelpos', None, INITOPT), |
| 17 | ('sticky', 'ew', INITOPT), |
| 18 | ) |
| 19 | self.defineoptions(kw, optiondefs) |
| 20 | |
| 21 | # Initialise the base class (after defining the options). |
| 22 | Pmw.MegaWidget.__init__(self, parent) |
| 23 | |
| 24 | # Create the components. |
| 25 | interior = self.interior() |
| 26 | |
| 27 | self._menubutton = self.createcomponent('menubutton', |
| 28 | (), None, |
| 29 | Tkinter.Menubutton, (interior,), |
| 30 | borderwidth = 2, |
| 31 | indicatoron = 1, |
| 32 | relief = 'raised', |
| 33 | anchor = 'c', |
| 34 | highlightthickness = 2, |
| 35 | direction = 'flush', |
| 36 | takefocus = 1, |
| 37 | ) |
| 38 | self._menubutton.grid(column = 2, row = 2, sticky = self['sticky']) |
| 39 | |
| 40 | self._menu = self.createcomponent('menu', |
| 41 | (), None, |
| 42 | Tkinter.Menu, (self._menubutton,), |
| 43 | tearoff=0 |
| 44 | ) |
| 45 | self._menubutton.configure(menu = self._menu) |
| 46 | |
| 47 | interior.grid_columnconfigure(2, weight = 1) |
| 48 | interior.grid_rowconfigure(2, weight = 1) |
| 49 | |
| 50 | # Create the label. |
| 51 | self.createlabel(interior) |
| 52 | |
| 53 | # Add the items specified by the initialisation option. |
| 54 | self._itemList = [] |
| 55 | self.setitems(self['items'], self['initialitem']) |
| 56 | |
| 57 | # Check keywords and initialise options. |
| 58 | self.initialiseoptions() |
| 59 | |
| 60 | def setitems(self, items, index = None): |
| 61 | |
| 62 | # Clean up old items and callback commands. |
| 63 | for oldIndex in range(len(self._itemList)): |
| 64 | tclCommandName = str(self._menu.entrycget(oldIndex, 'command')) |
| 65 | if tclCommandName != '': |
| 66 | self._menu.deletecommand(tclCommandName) |
| 67 | self._menu.delete(0, 'end') |
| 68 | self._itemList = list(items) |
| 69 | |
| 70 | # Set the items in the menu component. |
| 71 | for item in items: |
| 72 | self._menu.add_command(label = item, |
| 73 | command = lambda self = self, item = item: self._invoke(item)) |
| 74 | |
| 75 | # Set the currently selected value. |
| 76 | if index is None: |
| 77 | var = str(self._menubutton.cget('textvariable')) |
| 78 | if var != '': |
| 79 | # None means do not change text variable. |
| 80 | return |
| 81 | if len(items) == 0: |
| 82 | text = '' |
| 83 | elif str(self._menubutton.cget('text')) in items: |
| 84 | # Do not change selection if it is still valid |
| 85 | return |
| 86 | else: |
| 87 | text = items[0] |
| 88 | else: |
| 89 | index = self.index(index) |
| 90 | text = self._itemList[index] |
| 91 | |
| 92 | self.setvalue(text) |
| 93 | |
| 94 | def getcurselection(self): |
| 95 | var = str(self._menubutton.cget('textvariable')) |
| 96 | if var == '': |
| 97 | return str(self._menubutton.cget('text')) |
| 98 | else: |
| 99 | return self._menu.tk.globalgetvar(var) |
| 100 | |
| 101 | def getvalue(self): |
| 102 | return self.getcurselection() |
| 103 | |
| 104 | def setvalue(self, text): |
| 105 | var = str(self._menubutton.cget('textvariable')) |
| 106 | if var == '': |
| 107 | self._menubutton.configure(text = text) |
| 108 | else: |
| 109 | self._menu.tk.globalsetvar(var, text) |
| 110 | |
| 111 | def index(self, index): |
| 112 | listLength = len(self._itemList) |
| 113 | if type(index) == types.IntType: |
| 114 | if index < listLength: |
| 115 | return index |
| 116 | else: |
| 117 | raise ValueError, 'index "%s" is out of range' % index |
| 118 | elif index is Pmw.END: |
| 119 | if listLength > 0: |
| 120 | return listLength - 1 |
| 121 | else: |
| 122 | raise ValueError, 'OptionMenu has no items' |
| 123 | else: |
| 124 | if index is Pmw.SELECT: |
| 125 | if listLength > 0: |
| 126 | index = self.getcurselection() |
| 127 | else: |
| 128 | raise ValueError, 'OptionMenu has no items' |
| 129 | if index in self._itemList: |
| 130 | return self._itemList.index(index) |
| 131 | raise ValueError, \ |
| 132 | 'bad index "%s": must be a ' \ |
| 133 | 'name, a number, Pmw.END or Pmw.SELECT' % (index,) |
| 134 | |
| 135 | def invoke(self, index = Pmw.SELECT): |
| 136 | index = self.index(index) |
| 137 | text = self._itemList[index] |
| 138 | |
| 139 | return self._invoke(text) |
| 140 | |
| 141 | def _invoke(self, text): |
| 142 | self.setvalue(text) |
| 143 | |
| 144 | command = self['command'] |
| 145 | if callable(command): |
| 146 | return command(text) |