historical/toontown-classic.git/panda/Pmw/Pmw_1_3/contrib/TreeBrowser.py

733 lines
30 KiB
Python
Raw Normal View History

2024-01-16 11:20:27 -06:00
#
# FILE: TreeBrowser.py
#
# DESCRIPTION:
# This file provides a generic hierarchical tree browser widget.
#
# AUTHOR: Steve Kinneberg <skinneberg@mvista.com>,
# MontaVista Software, Inc. <source@mvista.com>
#
# Copyright 2001 MontaVista Software Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 675 Mass Ave, Cambridge, MA 02139, USA.
#
import types
import Tkinter
import Pmw
class _Branching:
def __init__(self):
# List of branch names
self._nodeNames = []
# Map from branch name to branch info
# branch Either _LeafNode or _BranchNode widget of the branch
# nodetype Either 'TreeNode' or 'LeafNode'
self._nodeAttrs = {}
def addbranch(self, branchName = None, **kw):
kw['indent'] = self['indent']
return apply(self._insertnode,
('tree', branchName, len(self._nodeNames),
self._treeRoot),
kw)
def addleaf(self, leafName = None, **kw):
return apply(self._insertnode,
('leaf', leafName, len(self._nodeNames),
self._treeRoot),
kw)
def insertbranch(self, branchName = None, before = 0, **kw):
kw['indent'] = self['indent']
return apply(self._insertnode,
('tree', branchName, before, self._treeRoot),
kw)
def insertleaf(self, leafName = None, before = 0, **kw):
return apply(self._insertnode,
('leaf', leafName, before, self._treeRoot),
kw)
def _insertnode(self, type, nodeName, before, treeRoot, **kw):
if 'selectbackground' not in kw.keys():
kw['selectbackground'] = self['selectbackground']
if 'selectforeground' not in kw.keys():
kw['selectforeground'] = self['selectforeground']
if 'background' not in kw.keys():
kw['background'] = self['background']
if 'foreground' not in kw.keys():
kw['foreground'] = self['foreground']
if nodeName == None:
nodeName = self._nodeName + ".%d" % (len(self._nodeNames) + 1)
if self._nodeAttrs.has_key(nodeName):
msg = 'Node "%s" already exists.' % nodeName
raise ValueError, msg
# Do this early to catch bad <before> spec before creating any items.
beforeIndex = self.index(before, 1)
attributes = {}
last = (beforeIndex == len(self._nodeNames))
if last and len(self._nodeNames) > 0:
# set the previous node to not last
self._nodeAttrs[self._nodeNames[-1]]['branch']._setlast(0)
if(type == 'tree'):
node = apply(self.createcomponent, ('branch%d'%len(self._nodeNames),
(), None,
_BranchNode,
self._branchFrame,
nodeName,
treeRoot,
self,
last,
), kw)
attributes['nodetype'] = 'TreeNode'
else:
node = apply(self.createcomponent, ('leaf%d'%len(self._nodeNames),
(), None,
_LeafNode,
self._branchFrame,
nodeName,
treeRoot,
self,
last,
), kw)
attributes['nodetype'] = 'LeafNode'
if len(self._nodeNames) == beforeIndex:
node.pack(anchor='w')
else:
bname = self._nodeNames[beforeIndex]
battrs = self._nodeAttrs[bname]
node.pack(anchor='w', before=battrs['branch'])
attributes['branch'] = node
self._nodeAttrs[nodeName] = attributes
self._nodeNames.insert(beforeIndex, nodeName)
self._sizechange()
return node
def delete(self, *nodes):
curSel = self._treeRoot.curselection()[0]
for node in nodes:
index = self.index(node)
name = self._nodeNames.pop(index)
dnode = self._nodeAttrs[name]['branch']
del self._nodeAttrs[name]
if dnode == curSel:
self._treeRoot._unhightlightnode(dnode)
dnode.destroy()
self._sizechange()
def destroy(self):
for node in len(self._nodeNames):
self.delete(node)
Pmw.MegaWidget.destroy(self)
def index(self, index, forInsert = 0):
if isinstance(index, _LeafNode):
index = index._nodeName
listLength = len(self._nodeNames)
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 type(index) == types.StringType:
if index in self._nodeNames:
return self._nodeNames.index(index)
raise ValueError, 'bad branch or leaf name: %s' % index
elif index is Pmw.END:
if forInsert:
return listLength
elif listLength > 0:
return listLength - 1
else:
raise ValueError, 'TreeNode has no branches'
#elif index is Pmw.SELECT:
# if listLength == 0:
# raise ValueError, 'TreeNode has no branches'
# return self._pageNames.index(self.getcurselection())
else:
validValues = 'a name, a number, Pmw.END, Pmw.SELECT, or a reference to a TreeBrowser Leaf or Branch'
raise ValueError, \
'bad index "%s": must be %s' % (index, validValues)
def getnodenames(self):
return self._nodeNames
def getnode(self, node):
nodeName = self._nodeNames[self.index(node)]
return self._nodeAttrs[nodeName]['branch']
class _LeafNode(Pmw.MegaWidget):
def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw):
colors = Pmw.Color.getdefaultpalette(parent)
self._nodeName = nodeName
self._treeRoot = treeRoot
self._parentNode = parentnode
self._last = last
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('selectbackground', colors['selectBackground'], INITOPT),
('selectforeground', colors['selectForeground'], INITOPT),
('background', colors['background'], INITOPT),
('foreground', colors['foreground'], INITOPT),
('selectcommand', None, None),
('deselectcommand', None, None),
('labelpos', 'e', INITOPT),
('labelmargin', 0, INITOPT),
('label', None, None),
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, parent)
# Create the components
interior = self._hull
labelpos = self['labelpos']
if self['label'] == None:
self._labelWidget = self.createcomponent('labelwidget',
(), None,
Pmw.LabeledWidget,
(interior,),
#background = self['background'],
#foreground = self['foreground'],
)
else:
self._labelWidget = self.createcomponent('labelwidget',
(), None,
Pmw.LabeledWidget,
(interior,),
label_background = self['background'],
label_foreground = self['foreground'],
labelpos = labelpos,
labelmargin = self['labelmargin'],
label_text = self['label'],
)
self._labelWidget.component('label').bind('<ButtonRelease-1>',
self._selectevent)
self._labelWidget.grid(column = 1, row = 0, sticky = 'w')
self._labelWidget.update()
self._labelheight = self._labelWidget.winfo_height()
self._lineCanvas = self.createcomponent('linecanvas',
(), None,
Tkinter.Canvas,
(interior,),
width = self._labelheight,
height = self._labelheight,
)
self._lineCanvas.grid( column = 0, row = 0, sticky = 'news')
self._lineCanvas.update()
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
self._lineCanvas.create_line(cw/2, ch/2, cw, ch/2, tag='hline')
if last:
self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline')
else:
self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline')
# Check keywords and initialise options.
self.initialiseoptions()
def interior(self):
return self._labelWidget.interior()
def select(self):
self._highlight()
def getname(self):
return self._nodeName
def getlabel(self):
return self['label']
def _selectevent(self, event):
self._highlight()
def _highlight(self):
self._treeRoot._highlightnode(self)
#self._subHull.configure(background = self._selectbg, relief = 'raised')
if self['label'] != None:
self._labelWidget.configure(label_background = self['selectbackground'])
self._labelWidget.configure(label_foreground = self['selectforeground'])
#self._viewButton.configure(background = self._selectbg)
cmd = self['selectcommand']
if callable(cmd):
cmd(self)
def _unhighlight(self):
#self._subHull.configure(background = self._bg, relief = 'flat')
if self['label'] != None:
self._labelWidget.configure(label_background = self['background'])
self._labelWidget.configure(label_foreground = self['foreground'])
#self._viewButton.configure(background = self._bg)
cmd = self['deselectcommand']
if callable(cmd):
cmd(self)
def _setlast(self, last):
self._last = last
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
if last:
self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline')
else:
self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline')
class _BranchNode(_LeafNode, _Branching): #Pmw.MegaWidget):
def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw):
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('view', 'collapsed', None),
('expandcommand', None, None),
('collapsecommand', None, None),
('indent', 0, INITOPT)
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
apply(_LeafNode.__init__,
(self, parent, nodeName, treeRoot, parentnode, last),
kw)
_Branching.__init__(self)
# Create the components
interior = self._hull
# Create the expand/collapse button
self._viewButton = self.createcomponent('viewbutton', (), None,
Tkinter.Canvas,
(interior,),
background = self['background'],
width = self._labelheight - 4,
height = self._labelheight - 4,
borderwidth = 2,
relief = 'raised')
self._viewButton.grid(column = 0, row = 0, sticky='se')
self._viewButton.bind('<ButtonPress-1>', self._showbuttonpress)
self._viewButton.bind('<ButtonRelease-1>', self._toggleview)
# The label widget is already created by the base class, however
# we do need to make some slight modifications.
if self['label'] != None:
self._labelWidget.component('label').bind('<Double-1>',
self._toggleview)
self._labelWidget.grid(column=1, row=0, columnspan = 3, sticky='sw')
# A line canvas is already created for us, we just need to make
# some slight modifications
self._lineCanvas.delete('hline')
self._lineCanvas.grid_forget()
# Set the minsize of column 1 to control additional branch frame indentation
self.grid_columnconfigure(1, minsize = self['indent'])
# Create the branch frame that will contain all the branch/leaf nodes
self._branchFrame = self.createcomponent('frame', (), None,
Tkinter.Frame, (interior,),
#borderwidth=2,
#relief='ridge',
)
self.grid_columnconfigure(2,minsize=0, weight=1)
#self.grid_rowconfigure(0,minsize=0)
if(self['view'] == 'expanded'):
Pmw.drawarrow(self._viewButton,
self['foreground'],
'down', 'arrow')
self._branchFrame.grid(column = 2, row = 1, sticky='nw')
if not self._last:
self._branchFrame.update()
bh = self._branchFrame.winfo_height()
self._lineCanvas.configure(height = bh)
self._lineCanvas.grid(column = 0, row = 1, sticky='news')
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
#self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag = 'vline')
self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
else:
Pmw.drawarrow(self._viewButton,
self['foreground'],
'right', 'arrow')
self._viewButton.configure(relief = 'raised')
# Check keywords and initialise options.
self.initialiseoptions()
def _showbuttonpress(self, event):
self._viewButton.configure(relief = 'sunken')
def _toggleview(self, event):
self._viewButton.configure(relief = 'sunken')
self.select()
if(self['view'] == 'expanded'):
self.collapsetree()
else:
self.expandtree()
self._viewButton.configure(relief = 'raised')
def expandtree(self):
if(self['view'] == 'collapsed'):
cmd = self['expandcommand']
if cmd is not None:
cmd(self)
self['view'] = 'expanded'
Pmw.drawarrow(self._viewButton,
self['foreground'],
'down', 'arrow')
self._branchFrame.grid(column = 2, row = 1, sticky='nw')
if not self._last:
self._branchFrame.update()
bh = self._branchFrame.winfo_height()
self._lineCanvas.configure(height = bh)
self._lineCanvas.grid(column = 0, row = 1, sticky='news')
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
#self._lineCanvas.create_line( cw/2, 1, cw/2, ch, tag = 'vline')
self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
self._parentNode._sizechange()
def collapsetree(self):
if(self['view'] == 'expanded'):
cmd = self['collapsecommand']
if cmd is not None:
cmd(self)
self['view'] = 'collapsed'
Pmw.drawarrow(self._viewButton,
self['foreground'],
'right', 'arrow')
self._branchFrame.grid_forget()
if not self._last:
#self._lineCanvas.delete('vline')
self._lineCanvas.grid_forget()
self._parentNode._sizechange()
def _setlast(self, last):
self._last = last
if self['view'] == 'expanded':
self._branchFrame.update()
bh = self._branchFrame.winfo_height()
self._lineCanvas.configure(height = bh)
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
self._lineCanvas.delete('vline')
if not last:
self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline')
def _sizechange(self):
if not self._last and self['view'] == 'expanded':
self._branchFrame.update()
bh = self._branchFrame.winfo_height()
self._lineCanvas.configure(height = bh)
if self._lineCanvas.coords('vline')[3] < bh:
cw = int(self._lineCanvas['width'])
ch = int(self._lineCanvas['height'])
#self._lineCanvas.delete('vline')
#self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline')
self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
self._parentNode._sizechange()
class TreeBrowser(Pmw.MegaWidget, _Branching):
def __init__(self, parent = None, nodeName = '0', **kw):
colors = Pmw.Color.getdefaultpalette(parent)
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('indent', 0, INITOPT),
('selectbackground', colors['selectBackground'], INITOPT),
('selectforeground', colors['selectForeground'], INITOPT),
('background', colors['background'], INITOPT),
('foreground', colors['foreground'], INITOPT),
#('selectrelief', 'raised', INITOPT),
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, parent)
_Branching.__init__(self)
# Create the components
interior = self._hull
browserFrame = self.createcomponent('frame', (), None,
Pmw.ScrolledFrame,
(interior,),
)
browserFrame.pack(expand = 1, fill='both')
self._branchFrame = browserFrame.interior()
self._highlightedNode = None
self._treeRoot = self
self._nodeName = nodeName
# Check keywords and initialise options.
self.initialiseoptions()
def _highlightnode(self, newNode):
if self._highlightedNode != newNode:
if self._highlightedNode != None:
self._highlightedNode._unhighlight()
self._highlightedNode = newNode
def _unhighlightnode(self):
if self._highlightedNode != None:
self._highlightedNode._unhighlight()
self._highlightedNode = None
def curselection(self):
retVal = None
if self._highlightedNode != None:
retVal = (self._highlightedNode,
self._highlightedNode._nodeName,
self._highlightedNode['label'])
return retVal
def getname(self):
return self._nodeName
# The top-level TreeBrowser widget only shows nodes in an expanded view
# but still provides collapsetree() and expandtree() methods so that users
# don't have to special case the top-level node
def collapsetree(self):
return
def expandtree(self):
return
def _sizechange(self):
return
if __name__ == '__main__':
rootWin = Tkinter.Tk()
Pmw.initialise()
rootWin.title('TreeBrowser Demo')
# Create the hierarchical tree browser widget
treeBrowser = TreeBrowser(rootWin,
#selectbackground = "darkgreen",
#selectforeground = 'lightgreen',
#background = 'green',
#indent = 10,
)
def printselected(node):
selection = treeBrowser.curselection()
if selection != None:
print "Selected node name:", selection[1], " label:", selection[2]
def printdeselected(node):
selection = treeBrowser.curselection()
if selection != None:
print "Deselected node name:", selection[1], " label:", selection[2]
def printexpanded(node):
print "Expanded node name:", node.getname(), " label:", node.getlabel()
def printcollapsed(node):
print "Collapsed node name:", node.getname(), " label:", node.getlabel()
for i in range(3):
# Add a tree node to the top level
treeNodeLevel1 = treeBrowser.addbranch(label = 'TreeNode %d'%i,
selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
)
for j in range(3):
# Add a tree node to the second level
treeNodeLevel2 = treeNodeLevel1.addbranch(label = 'TreeNode %d.%d'%(i,j),
#selectforeground = 'yellow',
selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
)
if i == 0 and j == 1:
dynamicTreeRootNode = treeNodeLevel1
dynamicTreePosNode = treeNodeLevel2
for item in range((i+1)*(j+1)):
# Add a leaf node to the third level
leaf = treeNodeLevel2.addleaf(label = "Item %c"%(item+65),
#selectbackground = 'blue',
selectcommand = printselected,
deselectcommand = printdeselected)
for item in range(i+1):
# Add a leaf node to the top level
leaf = treeNodeLevel1.addleaf(label = "Item %c"%(item+65),
selectcommand = printselected,
deselectcommand = printdeselected)
treeNodeLevel1 = treeBrowser.addbranch(label = 'Check Button Label',
selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
)
checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(),
text = 'Da Check Button',
relief = 'ridge',
command = treeNodeLevel1.select)
checkButton.pack()
treeNodeLevel1.addleaf(label = 'Labeled Leaf',
selectcommand = printselected,
deselectcommand = printdeselected)
leaf = treeNodeLevel1.addleaf(label = 'Labeled Leaf w/ Checkbutton',
selectcommand = printselected,
deselectcommand = printdeselected)
checkButton = Tkinter.Checkbutton(leaf.interior(),
text = 'Da Check Button',
relief = 'ridge',
command = leaf.select)
checkButton.pack()
treeNodeLevel1 = treeBrowser.addbranch(selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
)
checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(),
text = 'Check Button with no label',
relief = 'ridge',
command = treeNodeLevel1.select)
checkButton.pack()
treeNodeLevel1 = treeBrowser.addbranch(label = 'Label',
selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
)
# setup dynamic tree node insertion and removal
class dynTree:
def __init__(self):
self.dyn = Tkinter.IntVar()
self.dtree = None
self.dLeaf = treeBrowser.addleaf(selectcommand = self.dynSelected,
deselectcommand = self.dynDeselected)
self.dCheckButton = Tkinter.Checkbutton(self.dLeaf.interior(),
text = 'Enable Dynamic Tree',
variable = self.dyn,
command = self.ChkBtnHandler)
self.dCheckButton.pack()
def dynSelected(self, node):
self.dCheckButton.configure(background = self.dLeaf.configure('selectbackground')[4])
printselected(node)
def dynDeselected(self, node):
self.dCheckButton.configure(background = self.dLeaf.configure('background')[4])
printdeselected(node)
def ChkBtnHandler(self):
self.dLeaf.select()
if self.dyn.get() == 1:
self.dtree = dynamicTreeRootNode.insertbranch(label = 'Dynamic Tree Node',
selectcommand = printselected,
deselectcommand = printdeselected,
expandcommand = printexpanded,
collapsecommand = printcollapsed,
before = dynamicTreePosNode)
self.dtree.addleaf(label = 'Dynamic Leaf 1',
selectcommand = printselected,
deselectcommand = printdeselected)
self.dtree.addleaf(label = 'Dynamic Leaf 2',
selectcommand = printselected,
deselectcommand = printdeselected)
else:
if self.dtree != None:
dynamicTreeRootNode.delete(self.dtree)
self.dtree = None
foo = dynTree()
treeBrowser.pack(expand = 1, fill='both')
exitButton = Tkinter.Button(rootWin, text="Quit", command=rootWin.quit)
exitButton.pack()
rootWin.mainloop()