| 1 | # Based on iwidgets2.2.0/buttonbox.itk code. |
| 2 | |
| 3 | import types |
| 4 | import Tkinter |
| 5 | import Pmw |
| 6 | |
| 7 | class ButtonBox(Pmw.MegaWidget): |
| 8 | def __init__(self, parent = None, **kw): |
| 9 | |
| 10 | # Define the megawidget options. |
| 11 | INITOPT = Pmw.INITOPT |
| 12 | optiondefs = ( |
| 13 | ('labelmargin', 0, INITOPT), |
| 14 | ('labelpos', None, INITOPT), |
| 15 | ('orient', 'horizontal', INITOPT), |
| 16 | ('padx', 3, INITOPT), |
| 17 | ('pady', 3, INITOPT), |
| 18 | ) |
| 19 | self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',)) |
| 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 | if self['labelpos'] is None: |
| 27 | self._buttonBoxFrame = self._hull |
| 28 | columnOrRow = 0 |
| 29 | else: |
| 30 | self._buttonBoxFrame = self.createcomponent('frame', |
| 31 | (), None, |
| 32 | Tkinter.Frame, (interior,)) |
| 33 | self._buttonBoxFrame.grid(column=2, row=2, sticky='nsew') |
| 34 | columnOrRow = 2 |
| 35 | |
| 36 | self.createlabel(interior) |
| 37 | |
| 38 | orient = self['orient'] |
| 39 | if orient == 'horizontal': |
| 40 | interior.grid_columnconfigure(columnOrRow, weight = 1) |
| 41 | elif orient == 'vertical': |
| 42 | interior.grid_rowconfigure(columnOrRow, weight = 1) |
| 43 | else: |
| 44 | raise ValueError, 'bad orient option ' + repr(orient) + \ |
| 45 | ': must be either \'horizontal\' or \'vertical\'' |
| 46 | |
| 47 | # Initialise instance variables. |
| 48 | |
| 49 | # List of tuples describing the buttons: |
| 50 | # - name |
| 51 | # - button widget |
| 52 | self._buttonList = [] |
| 53 | |
| 54 | # The index of the default button. |
| 55 | self._defaultButton = None |
| 56 | |
| 57 | self._timerId = None |
| 58 | |
| 59 | # Check keywords and initialise options. |
| 60 | self.initialiseoptions() |
| 61 | |
| 62 | def destroy(self): |
| 63 | if self._timerId: |
| 64 | self.after_cancel(self._timerId) |
| 65 | self._timerId = None |
| 66 | Pmw.MegaWidget.destroy(self) |
| 67 | |
| 68 | def numbuttons(self): |
| 69 | return len(self._buttonList) |
| 70 | |
| 71 | def index(self, index, forInsert = 0): |
| 72 | listLength = len(self._buttonList) |
| 73 | if type(index) == types.IntType: |
| 74 | if forInsert and index <= listLength: |
| 75 | return index |
| 76 | elif not forInsert and index < listLength: |
| 77 | return index |
| 78 | else: |
| 79 | raise ValueError, 'index "%s" is out of range' % index |
| 80 | elif index is Pmw.END: |
| 81 | if forInsert: |
| 82 | return listLength |
| 83 | elif listLength > 0: |
| 84 | return listLength - 1 |
| 85 | else: |
| 86 | raise ValueError, 'ButtonBox has no buttons' |
| 87 | elif index is Pmw.DEFAULT: |
| 88 | if self._defaultButton is not None: |
| 89 | return self._defaultButton |
| 90 | raise ValueError, 'ButtonBox has no default' |
| 91 | else: |
| 92 | names = map(lambda t: t[0], self._buttonList) |
| 93 | if index in names: |
| 94 | return names.index(index) |
| 95 | validValues = 'a name, a number, Pmw.END or Pmw.DEFAULT' |
| 96 | raise ValueError, \ |
| 97 | 'bad index "%s": must be %s' % (index, validValues) |
| 98 | |
| 99 | def insert(self, componentName, beforeComponent = 0, **kw): |
| 100 | if componentName in self.components(): |
| 101 | raise ValueError, 'button "%s" already exists' % componentName |
| 102 | if not kw.has_key('text'): |
| 103 | kw['text'] = componentName |
| 104 | kw['default'] = 'normal' |
| 105 | button = apply(self.createcomponent, (componentName, |
| 106 | (), 'Button', |
| 107 | Tkinter.Button, (self._buttonBoxFrame,)), kw) |
| 108 | |
| 109 | index = self.index(beforeComponent, 1) |
| 110 | horizontal = self['orient'] == 'horizontal' |
| 111 | numButtons = len(self._buttonList) |
| 112 | |
| 113 | # Shift buttons up one position. |
| 114 | for i in range(numButtons - 1, index - 1, -1): |
| 115 | widget = self._buttonList[i][1] |
| 116 | pos = i * 2 + 3 |
| 117 | if horizontal: |
| 118 | widget.grid(column = pos, row = 0) |
| 119 | else: |
| 120 | widget.grid(column = 0, row = pos) |
| 121 | |
| 122 | # Display the new button. |
| 123 | if horizontal: |
| 124 | button.grid(column = index * 2 + 1, row = 0, sticky = 'ew', |
| 125 | padx = self['padx'], pady = self['pady']) |
| 126 | self._buttonBoxFrame.grid_columnconfigure( |
| 127 | numButtons * 2 + 2, weight = 1) |
| 128 | else: |
| 129 | button.grid(column = 0, row = index * 2 + 1, sticky = 'ew', |
| 130 | padx = self['padx'], pady = self['pady']) |
| 131 | self._buttonBoxFrame.grid_rowconfigure( |
| 132 | numButtons * 2 + 2, weight = 1) |
| 133 | self._buttonList.insert(index, (componentName, button)) |
| 134 | |
| 135 | return button |
| 136 | |
| 137 | def add(self, componentName, **kw): |
| 138 | return apply(self.insert, (componentName, len(self._buttonList)), kw) |
| 139 | |
| 140 | def delete(self, index): |
| 141 | index = self.index(index) |
| 142 | (name, widget) = self._buttonList[index] |
| 143 | widget.grid_forget() |
| 144 | self.destroycomponent(name) |
| 145 | |
| 146 | numButtons = len(self._buttonList) |
| 147 | |
| 148 | # Shift buttons down one position. |
| 149 | horizontal = self['orient'] == 'horizontal' |
| 150 | for i in range(index + 1, numButtons): |
| 151 | widget = self._buttonList[i][1] |
| 152 | pos = i * 2 - 1 |
| 153 | if horizontal: |
| 154 | widget.grid(column = pos, row = 0) |
| 155 | else: |
| 156 | widget.grid(column = 0, row = pos) |
| 157 | |
| 158 | if horizontal: |
| 159 | self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 - 1, |
| 160 | minsize = 0) |
| 161 | self._buttonBoxFrame.grid_columnconfigure(numButtons * 2, weight = 0) |
| 162 | else: |
| 163 | self._buttonBoxFrame.grid_rowconfigure(numButtons * 2, weight = 0) |
| 164 | del self._buttonList[index] |
| 165 | |
| 166 | def setdefault(self, index): |
| 167 | # Turn off the default ring around the current default button. |
| 168 | if self._defaultButton is not None: |
| 169 | button = self._buttonList[self._defaultButton][1] |
| 170 | button.configure(default = 'normal') |
| 171 | self._defaultButton = None |
| 172 | |
| 173 | # Turn on the default ring around the new default button. |
| 174 | if index is not None: |
| 175 | index = self.index(index) |
| 176 | self._defaultButton = index |
| 177 | button = self._buttonList[index][1] |
| 178 | button.configure(default = 'active') |
| 179 | |
| 180 | def invoke(self, index = Pmw.DEFAULT, noFlash = 0): |
| 181 | # Invoke the callback associated with the *index* button. If |
| 182 | # *noFlash* is not set, flash the button to indicate to the |
| 183 | # user that something happened. |
| 184 | |
| 185 | button = self._buttonList[self.index(index)][1] |
| 186 | if not noFlash: |
| 187 | state = button.cget('state') |
| 188 | relief = button.cget('relief') |
| 189 | button.configure(state = 'active', relief = 'sunken') |
| 190 | self.update_idletasks() |
| 191 | self.after(100) |
| 192 | button.configure(state = state, relief = relief) |
| 193 | return button.invoke() |
| 194 | |
| 195 | def button(self, buttonIndex): |
| 196 | return self._buttonList[self.index(buttonIndex)][1] |
| 197 | |
| 198 | def alignbuttons(self, when = 'later'): |
| 199 | if when == 'later': |
| 200 | if not self._timerId: |
| 201 | self._timerId = self.after_idle(self.alignbuttons, 'now') |
| 202 | return |
| 203 | self.update_idletasks() |
| 204 | self._timerId = None |
| 205 | |
| 206 | # Determine the width of the maximum length button. |
| 207 | max = 0 |
| 208 | horizontal = (self['orient'] == 'horizontal') |
| 209 | for index in range(len(self._buttonList)): |
| 210 | gridIndex = index * 2 + 1 |
| 211 | if horizontal: |
| 212 | width = self._buttonBoxFrame.grid_bbox(gridIndex, 0)[2] |
| 213 | else: |
| 214 | width = self._buttonBoxFrame.grid_bbox(0, gridIndex)[2] |
| 215 | if width > max: |
| 216 | max = width |
| 217 | |
| 218 | # Set the width of all the buttons to be the same. |
| 219 | if horizontal: |
| 220 | for index in range(len(self._buttonList)): |
| 221 | self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1, |
| 222 | minsize = max) |
| 223 | else: |
| 224 | self._buttonBoxFrame.grid_columnconfigure(0, minsize = max) |