#!/usr/bin/env python
#
# $Id: MCScrolledListBox.py,v 1.4 2001/11/03 11:04:26 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""Multi-column scrolled list box.
A scrolled listbox with multiple columns, suitable for use as a
table display.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: MCScrolledListBox.py,v $',
'rcs_id' : '$Id: MCScrolledListBox.py,v 1.4 2001/11/03 11:04:26 doughellmann Exp $',
'creator' : 'Doug Hellmann <doug@hellfly.net>',
'project' : 'PmwContribD',
'created' : 'Sun, 01-Apr-2001 13:04:12 EDT',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.4 $',
'date' : '$Date: 2001/11/03 11:04:26 $',
}
#
# Import system modules
#
from Tkinter import *
import Pmw
import types
#
# Import Local modules
#
#
# Module
#
class MCScrolledListBox(Pmw.ScrolledFrame):
"""Multi-column scrolled list box.
Options
'columns' -- The number of columns to be created.
'dblclickcommand' -- Function to be called when user double
clicks on a list entry.
'selectioncommand' -- Function to be called when the user
selects something in the list.
"""
def __init__(self, parent=None, **kw):
INITOPT = Pmw.INITOPT
optiondefs = (
('columns', 1, INITOPT),
('vertflex', 'elastic', self._vertflex),
# ('horizflex', 'shrink', self._horizflex),
('horizflex', 'expand', self._horizflex),
('dblclickcommand', None, INITOPT),
('selectioncommand', None, INITOPT),
)
self.defineoptions(kw, optiondefs)
Pmw.ScrolledFrame.__init__(self, parent=parent)
# Define bindings for this class
tag = 'MCSLBSelect' + str(self)
self.tag_name = tag
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.bind_class(tag, '<Button-1>', self._propogateInitialSelection)
self.bind_class(tag, '<B1-Motion>', self._propogateSelection)
# Create the scrolled lists
interior = self.interior()
self.lists = []
# Create the scrollbar to manage all of the
# Listboxes.
sbf = Frame(self.origInterior)
padding = Label(sbf)
padding.pack(
side=TOP,
expand=NO,
fill=X,
)
sb = self.createcomponent(
'vertscrollbar',
(), None,
Scrollbar,
(sbf,),
orient='vertical',
command=self._yview,
)
sb.pack(
side=TOP,
expand=YES,
fill=Y,
)
sbf.grid(
row=2,
column=4,
sticky='news',
)
self.origInterior.grid_columnconfigure(3, minsize=0)
return
def addColumn(self, label='Column',
relief=RAISED,
bd=1,
expand=YES,
width=None,
):
"Adds a column to the widget when it is created."
f = Frame(self.interior(), width=width)
clt = label
l = self.createcomponent(
'%slabel' % clt,
(), None,
Label,
(f,),
text=clt,
relief=relief,
bd=bd,
width=width,
)
l.pack(side=TOP, expand=NO, fill=X,)
lb = self.createcomponent(
'%slistbox' % clt,
(), None,
Listbox,
(f,),
bd=0,
relief=SOLID,
highlightthickness=0,
selectmode=EXTENDED,
exportselection=FALSE,
yscrollcommand=self._yset,
width=width,
)
lb.bindtags(lb.bindtags() + (self.tag_name,))
self.lists.append(lb)
lb.pack(side=TOP, expand=YES, fill=BOTH,)
f.pack(side=LEFT, expand=expand, fill=BOTH,)
return
##
## SCROLLBAR METHODS
##
def _yset(self, *args):
"Fixup method for scrollbar handling."
#print '_yset', args
apply(self.component('vertscrollbar').set, args)
for lb in self.lists:
lb.yview_moveto(args[0])
return
def _yview(self, *args):
"Fixup method for scrollbar handling."
for lb in self.lists:
apply(lb.yview, args)
return
##
## SELECTION METHODS
##
def _clearSelection(self, event=None):
"Remove the current selection."
for lb in self.lists:
lb.selection_clear(0, 'end')
return
def _makeSelection(self, event=None):
"Set the selection on all columns and call the callback."
self._propogateSelection(event=event)
command = self['selectioncommand']
if callable(command):
command()
return
def _makeInitialSelection(self, event=None):
"Start a selection."
self._propogateInitialSelection(event=event)
return
def _propogateInitialSelection(self, event=None):
"Copy initial selection to all columns."
clicked_on = event.widget.nearest(event.y)
self._clearSelection()
self._propogateSelection(idxs = [ clicked_on ])
return
def _propogateSelection(self, event=None, idxs=None):
"Copy current selection to all columns."
if not idxs:
idxs = map(int, event.widget.curselection())
self._clearSelection()
if idxs:
for lb in self.lists:
for pos in idxs:
lb.selection_set(pos)
return
##
## CALLBACK BINDINGS
##
def _doubleClick(self, event=None):
"Handle mouse double click."
command = self['dblclickcommand']
if callable(command):
command()
return
##
## PUBLIC METHODS
##
def curselection(self):
"Returns the current selection range."
return self.lists[0].curselection()
def getcurselection(self):
selection = []
#for lb in self.lists:
# print lb.curselection()
selection_range = self.curselection()
l = []
for i in selection_range:
l.append(self.get(i, i)[0])
return tuple(l)
def setlists(self, itemlists):
"Set the contents of the widget. Takes a sequence of lists."
#print 'got %d lists' % len(itemlists)
for idx in range(0, len(itemlists)):
items = itemlists[idx]
lb = self.lists[idx]
lb.delete(0, 'end')
if len(items) > 0:
if type(items) != types.TupleType:
items = tuple(items)
apply(lb.insert, (0,) + items)
return
def get(self, first=None, last=None):
"Returns the values currently selected."
if not first and not last:
return []
#print 'GET: ', first, '->', last
if first == last:
list_items = map(lambda x, f=first: x.get(f), self.lists)
return ( tuple(list_items), )
else:
list_items = map(lambda x, f=first, l=last:x.get(f,l), self.lists)
return tuple(apply( map, (None,) + tuple(list_items) ))
##
##
##
if __name__ == '__main__':
import GuiAppD
class mclisttest(GuiAppD.GuiAppD):
def createInterface(self):
w = self.createcomponent(
'mclisttest',
(), None,
MCScrolledListBox,
(self.interior(),),
selectioncommand=self.selectioncommand,
dblclickcommand=self.dblclickcommand,
)
for cl, expand, width in (
('Zero', NO, 30),
('One', YES, 10),
('Two', NO, 30),
('Three', YES, 60),
('Four', NO, 30),
):
w.addColumn(label=cl, expand=expand, width=width)
w.pack(
side=TOP,
expand=YES,
fill=BOTH,
)
testlist = ('zero', 'one', 'two', 'three', 'four',
'five', 'six', 'seven', 'eight', 'nine', 'ten',
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen')
w.setlists(
map( lambda num, l=testlist: map( lambda x, num=num: \
'%d_%s' % (num, x),
l ),
range(0, 5)
)
)
return
def selectioncommand(self, *args):
print 'selectioncommand:', args
print '\tselected: '
for rec in self.component('mclisttest').getcurselection():
print '\t', rec
return
def dblclickcommand(self, *args):
print 'dblclickcommand:', args
print '\tselected: '
for rec in self.component('mclisttest').getcurselection():
print '\t', rec
return
mclisttest().run()
|