# Based on iwidgets2.2.0/scrolledlistbox.itk code.
import types
import Tkinter
import Pmw
class ScrolledListBox(Pmw.MegaWidget):
def __init__(self, parent = None, **kw):
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('dblclickcommand', '', None),
('hscrollmode', 'dynamic', self._hscrollMode),
('items', (), INITOPT),
('labelmargin', 0, INITOPT),
('labelpos', None, INITOPT),
('scrollmargin', 2, INITOPT),
('selectioncommand', '', None),
('vscrollmode', 'dynamic', self._vscrollMode),
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, parent)
# Create the components.
interior = self.interior()
# Create the listbox widget.
self._listbox = self.createcomponent('listbox',
(), None,
Tkinter.Listbox, (interior,),
xscrollcommand=self._scrollListX,
yscrollcommand=self._scrollListY)
self._listbox.grid(row = 2, column = 2, sticky = 'news')
interior.grid_rowconfigure(2, weight = 1, minsize = 0)
interior.grid_columnconfigure(2, weight = 1, minsize = 0)
# Create the vertical scrollbar
self._vertScrollbar = self.createcomponent('vertscrollbar',
(), 'Scrollbar',
Tkinter.Scrollbar, (interior,),
orient='vertical', command=self._listbox.yview)
# Create the horizontal scrollbar
self._horizScrollbar = self.createcomponent('horizscrollbar',
(), 'Scrollbar',
Tkinter.Scrollbar, (interior,),
orient='horizontal', command=self._listbox.xview)
self.createlabel(interior, childCols = 3, childRows = 3)
# Add the items specified by the initialisation option.
items = self['items']
if type(items) != types.TupleType:
items = tuple(items)
if len(items) > 0:
apply(self._listbox.insert, ('end',) + items)
tag = 'SLBSelect' + str(self)
self.bind_class(tag, '<Control-Key-backslash>', self._makeSelection)
self.bind_class(tag, '<Control-Key-slash>', self._makeSelection)
self.bind_class(tag, '<Key-Escape>', self._makeSelection)
self.bind_class(tag, '<Shift-Key-Select>', self._makeSelection)
self.bind_class(tag, '<Control-Shift-Key-space>', self._makeSelection)
self.bind_class(tag, '<Key-Select>', self._makeSelection)
self.bind_class(tag, '<Key-space>', self._makeSelection)
self.bind_class(tag, '<Control-Shift-Key-End>', self._makeSelection)
self.bind_class(tag, '<Control-Key-End>', self._makeSelection)
self.bind_class(tag, '<Control-Shift-Key-Home>', self._makeSelection)
self.bind_class(tag, '<Control-Key-Home>', self._makeSelection)
self.bind_class(tag, '<Shift-Key-Down>', self._makeSelection)
self.bind_class(tag, '<Shift-Key-Up>', self._makeSelection)
self.bind_class(tag, '<Control-Button-1>', self._makeSelection)
self.bind_class(tag, '<Shift-Button-1>', self._makeSelection)
self.bind_class(tag, '<ButtonRelease-1>', self._makeSelection)
self.bind_class(tag, '<Double-1>', self._doubleClick)
self._listbox.bindtags(self._listbox.bindtags() + (tag,))
# Initialise instance variables.
self._vertMode = None
self._horizMode = None
# Check keywords and initialise options.
self.initialiseoptions(ScrolledListBox)
# ======================================================================
# Configuration methods.
def _vscrollMode(self):
mode = self['vscrollmode']
if mode == 'static':
self._vertScrollbarDisplay(1)
elif mode == 'dynamic' or mode == 'none':
self._vertScrollbarDisplay(0)
else:
message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
raise ValueError, message
def _hscrollMode(self):
mode = self['hscrollmode']
if mode == 'static':
self._horizScrollbarDisplay(1)
elif mode == 'dynamic' or mode == 'none':
self._horizScrollbarDisplay(0)
else:
message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
raise ValueError, message
# ======================================================================
# Public methods.
def getcurselection(self):
rtn = []
for sel in self.curselection():
rtn.append(self._listbox.get(sel))
return tuple(rtn)
def justify(self, direction):
if direction == 'left':
self._listbox.xview('moveto', 0)
elif direction == 'right':
self._listbox.xview('moveto', 1)
elif direction == 'top':
self._listbox.yview('moveto', 0)
elif direction == 'bottom':
self._listbox.yview('moveto', 1)
else:
validValues = 'left, right, top, or bottom'
raise ValueError, \
'bad justify argument "%s": should be %s' \
% (direction, validValues)
def setlist(self, items):
self._listbox.delete(0, 'end')
if len(items) > 0:
if type(items) != types.TupleType:
items = tuple(items)
apply(self._listbox.insert, (0,) + items)
# ======================================================================
# Private methods.
def _makeSelection(self, event):
command = self['selectioncommand']
if callable(command):
command()
def _doubleClick(self, event):
command = self['dblclickcommand']
if callable(command):
command()
def _vertScrollbarDisplay(self, mode):
if mode != self._vertMode:
self._vertMode = mode
margin = self['scrollmargin']
interior = self.interior()
if self._vertMode:
self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
interior.grid_columnconfigure(3, minsize = margin)
else:
self._vertScrollbar.grid_forget()
interior.grid_columnconfigure(3, minsize = 0)
def _horizScrollbarDisplay(self, mode):
if mode != self._horizMode:
self._horizMode = mode
margin = self['scrollmargin']
interior = self.interior()
if self._horizMode:
self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
interior.grid_rowconfigure(3, minsize = margin)
else:
self._horizScrollbar.grid_forget()
interior.grid_rowconfigure(3, minsize = 0)
def _scrollListX(self, first, last):
self._horizScrollbar.set(first, last)
if self['hscrollmode'] == 'dynamic':
if first == '0' and last == '1':
self._horizScrollbarDisplay(0)
else:
self._horizScrollbarDisplay(1)
def _scrollListY(self, first, last):
self._vertScrollbar.set(first, last)
if self['vscrollmode'] == 'dynamic':
if first == '0' and last == '1':
self._vertScrollbarDisplay(0)
else:
self._vertScrollbarDisplay(1)
# Need to explicitly forward this to override the stupid
# (grid_)size method inherited from Tkinter.Frame.Grid.
def size(self):
return self._listbox.size()
Pmw.forwardmethods(ScrolledListBox, Tkinter.Listbox, '_listbox')