225 lines
6.8 KiB
Python
225 lines
6.8 KiB
Python
|
# Based on iwidgets2.2.0/buttonbox.itk code.
|
||
|
|
||
|
import types
|
||
|
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', 3, INITOPT),
|
||
|
('pady', 3, INITOPT),
|
||
|
)
|
||
|
self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
|
||
|
|
||
|
# 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)
|
||
|
|
||
|
orient = self['orient']
|
||
|
if orient == 'horizontal':
|
||
|
interior.grid_columnconfigure(columnOrRow, weight = 1)
|
||
|
elif orient == 'vertical':
|
||
|
interior.grid_rowconfigure(columnOrRow, weight = 1)
|
||
|
else:
|
||
|
raise ValueError, 'bad orient option ' + repr(orient) + \
|
||
|
': must be either \'horizontal\' or \'vertical\''
|
||
|
|
||
|
# Initialise instance variables.
|
||
|
|
||
|
# List of tuples describing the buttons:
|
||
|
# - name
|
||
|
# - button widget
|
||
|
self._buttonList = []
|
||
|
|
||
|
# The index of the default button.
|
||
|
self._defaultButton = None
|
||
|
|
||
|
self._timerId = None
|
||
|
|
||
|
# Check keywords and initialise options.
|
||
|
self.initialiseoptions()
|
||
|
|
||
|
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) == types.IntType:
|
||
|
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 is Pmw.END:
|
||
|
if forInsert:
|
||
|
return listLength
|
||
|
elif listLength > 0:
|
||
|
return listLength - 1
|
||
|
else:
|
||
|
raise ValueError, 'ButtonBox has no buttons'
|
||
|
elif index is Pmw.DEFAULT:
|
||
|
if self._defaultButton is not None:
|
||
|
return self._defaultButton
|
||
|
raise ValueError, 'ButtonBox has no default'
|
||
|
else:
|
||
|
names = map(lambda t: t[0], self._buttonList)
|
||
|
if index in names:
|
||
|
return names.index(index)
|
||
|
validValues = 'a name, a number, Pmw.END or Pmw.DEFAULT'
|
||
|
raise ValueError, \
|
||
|
'bad index "%s": must be %s' % (index, validValues)
|
||
|
|
||
|
def insert(self, componentName, beforeComponent = 0, **kw):
|
||
|
if componentName in self.components():
|
||
|
raise ValueError, 'button "%s" already exists' % componentName
|
||
|
if not kw.has_key('text'):
|
||
|
kw['text'] = componentName
|
||
|
kw['default'] = 'normal'
|
||
|
button = apply(self.createcomponent, (componentName,
|
||
|
(), 'Button',
|
||
|
Tkinter.Button, (self._buttonBoxFrame,)), kw)
|
||
|
|
||
|
index = self.index(beforeComponent, 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][1]
|
||
|
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:
|
||
|
button.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:
|
||
|
button.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, (componentName, button))
|
||
|
|
||
|
return button
|
||
|
|
||
|
def add(self, componentName, **kw):
|
||
|
return apply(self.insert, (componentName, len(self._buttonList)), kw)
|
||
|
|
||
|
def delete(self, index):
|
||
|
index = self.index(index)
|
||
|
(name, widget) = self._buttonList[index]
|
||
|
widget.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):
|
||
|
widget = self._buttonList[i][1]
|
||
|
pos = i * 2 - 1
|
||
|
if horizontal:
|
||
|
widget.grid(column = pos, row = 0)
|
||
|
else:
|
||
|
widget.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:
|
||
|
button = self._buttonList[self._defaultButton][1]
|
||
|
button.configure(default = 'normal')
|
||
|
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
|
||
|
button = self._buttonList[index][1]
|
||
|
button.configure(default = 'active')
|
||
|
|
||
|
def invoke(self, index = Pmw.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 button(self, buttonIndex):
|
||
|
return self._buttonList[self.index(buttonIndex)][1]
|
||
|
|
||
|
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
|
||
|
|
||
|
# Determine the width of the maximum length button.
|
||
|
max = 0
|
||
|
horizontal = (self['orient'] == 'horizontal')
|
||
|
for index in range(len(self._buttonList)):
|
||
|
gridIndex = index * 2 + 1
|
||
|
if horizontal:
|
||
|
width = self._buttonBoxFrame.grid_bbox(gridIndex, 0)[2]
|
||
|
else:
|
||
|
width = self._buttonBoxFrame.grid_bbox(0, gridIndex)[2]
|
||
|
if width > max:
|
||
|
max = width
|
||
|
|
||
|
# Set the width of all the buttons to be the same.
|
||
|
if horizontal:
|
||
|
for index in range(len(self._buttonList)):
|
||
|
self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1,
|
||
|
minsize = max)
|
||
|
else:
|
||
|
self._buttonBoxFrame.grid_columnconfigure(0, minsize = max)
|