# Based on iwidgets2.2.0/buttonbox.itk code. import Tkinter import Pmw class ButtonBox(Pmw.MegaWidget): def __init__(self, parent = None, **kw): # Define the megawidget options. INITOPT = Pmw.INITOPT optiondefs = ( ('labelmargin', 0, INITOPT), ('labelpos', None, INITOPT), ('orient', 'horizontal', INITOPT), ('padx', 8, INITOPT), ('pady', 8, INITOPT), ('ringborderwidth', 1, INITOPT), ('ringpadx', 1, INITOPT), ('ringpady', 1, INITOPT), ) self.defineoptions(kw, optiondefs) # Initialise the base class (after defining the options). Pmw.MegaWidget.__init__(self, parent) # Create the components. interior = self.interior() if self['labelpos'] is None: self._buttonBoxFrame = self._hull columnOrRow = 0 else: self._buttonBoxFrame = self.createcomponent('frame', (), None, Tkinter.Frame, (interior,)) self._buttonBoxFrame.grid(column=2, row=2, sticky='nsew') columnOrRow = 2 self.createlabel(interior) if self['orient'] == 'horizontal': interior.grid_columnconfigure(columnOrRow, weight = 1) else: interior.grid_rowconfigure(columnOrRow, weight = 1) # Initialise instance variables. # List of tuples describing the buttons: # - name # - button widget # - surrounding frame widget self._buttonList = [] # The index of the default button. self._defaultButton = None self._timerId = None # Check keywords and initialise options. self.initialiseoptions(ButtonBox) def destroy(self): if self._timerId: self.after_cancel(self._timerId) self._timerId = None Pmw.MegaWidget.destroy(self) def numbuttons(self): return len(self._buttonList) def index(self, index, forInsert = 0): listLength = len(self._buttonList) if type(index) == type(1): if forInsert and index <= listLength: return index elif not forInsert and index < listLength: return index else: raise ValueError, 'index "%s" is out of range' % index elif index == 'end': if forInsert: return listLength elif listLength > 0: return listLength - 1 else: raise ValueError, 'ButtonBox has no buttons' elif index == 'default': if self._defaultButton is not None: return self._defaultButton raise ValueError, 'ButtonBox has no default' else: for count in range(listLength): name = self._buttonList[count][0] if index == name: return count validValues = 'number, end, default, or a name' raise ValueError, \ 'bad index "%s": must be %s' % (index, validValues) def insert(self, name, before = 0, **kw): if name in self.components(): raise ValueError, 'button "%s" already exists' % name frame = Tkinter.Frame(self._buttonBoxFrame, borderwidth = self['ringborderwidth'], relief = 'flat') if not kw.has_key('text'): kw['text'] = name button = apply(self.createcomponent, (name, (), 'Button', Tkinter.Button, (frame,)), kw) button.pack(padx = self['ringpadx'], pady = self['ringpady'], fill = 'both', expand = 1) index = self.index(before, 1) horizontal = self['orient'] == 'horizontal' numButtons = len(self._buttonList) # Shift buttons up one position. for i in range(numButtons - 1, index - 1, -1): widget = self._buttonList[i][2] pos = i * 2 + 3 if horizontal: widget.grid(column = pos, row = 0) else: widget.grid(column = 0, row = pos) # Display the new button. if horizontal: frame.grid(column = index * 2 + 1, row = 0, sticky = 'ew', padx = self['padx'], pady = self['pady']) self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 + 2, weight = 1) else: frame.grid(column = 0, row = index * 2 + 1, sticky = 'ew', padx = self['padx'], pady = self['pady']) self._buttonBoxFrame.grid_rowconfigure(numButtons * 2 + 2, weight = 1) self._buttonList.insert(index, (name, button, frame)) return button def add(self, name, **kw): return apply(self.insert, (name, len(self._buttonList)), kw) def delete(self, index): index = self.index(index) (name, widget, frame) = self._buttonList[index] frame.grid_forget() self.destroycomponent(name) numButtons = len(self._buttonList) # Shift buttons down one position. horizontal = self['orient'] == 'horizontal' for i in range(index + 1, numButtons): frame = self._buttonList[i][2] pos = i * 2 - 1 if horizontal: frame.grid(column = pos, row = 0) else: frame.grid(column = 0, row = pos) if horizontal: self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 - 1, minsize = 0) self._buttonBoxFrame.grid_columnconfigure(numButtons * 2, weight = 0) else: self._buttonBoxFrame.grid_rowconfigure(numButtons * 2, weight = 0) del self._buttonList[index] def setdefault(self, index): # Turn off the default ring around the current default button. if self._defaultButton is not None: frame = self._buttonList[self._defaultButton][2] frame.configure(relief = 'flat') self._defaultButton = None # Turn on the default ring around the new default button. if index is not None: index = self.index(index) self._defaultButton = index frame = self._buttonList[index][2] frame.configure(relief = 'sunken') def invoke(self, index = 'default', noFlash = 0): # Invoke the callback associated with the *index* button. If # *noFlash* is not set, flash the button to indicate to the # user that something happened. button = self._buttonList[self.index(index)][1] if not noFlash: state = button.cget('state') relief = button.cget('relief') button.configure(state = 'active', relief = 'sunken') self.update_idletasks() self.after(100) button.configure(state = state, relief = relief) return button.invoke() def alignbuttons(self, when = 'later'): if when == 'later': if not self._timerId: self._timerId = self.after_idle(self.alignbuttons, 'now') return self.update_idletasks() self._timerId = None if self['orient'] == 'horizontal': # Determine the width of the maximum length button. max = 0 for index in range(len(self._buttonList)): width = self._buttonBoxFrame.grid_bbox(index * 2 + 1, 0)[2] if width > max: max = width # Set the widths of all the buttons to be the same. for index in range(len(self._buttonList)): self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1, minsize = max) else: # Determine the width of the maximum length button. max = 0 for index in range(len(self._buttonList)): width = self._buttonBoxFrame.grid_bbox(0, index * 2 + 1)[2] if width > max: max = width # Set the width of all the buttons to be the same. self._buttonBoxFrame.grid_columnconfigure(0, minsize = max)