# 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.
# Map from branch name to branch info
# branch Either _LeafNode or _BranchNode widget of the branch
# nodetype Either 'TreeNode' or 'LeafNode'
def addbranch(self
, branchName
= None, **kw
):
kw
['indent'] = self
['indent']
return apply(self
._insertnode
,
('tree', branchName
, len(self
._nodeNames
),
def addleaf(self
, leafName
= None, **kw
):
return apply(self
._insertnode
,
('leaf', leafName
, len(self
._nodeNames
),
def insertbranch(self
, branchName
= None, before
= 0, **kw
):
kw
['indent'] = self
['indent']
return apply(self
._insertnode
,
('tree', branchName
, before
, self
._treeRoot
),
def insertleaf(self
, leafName
= None, before
= 0, **kw
):
return apply(self
._insertnode
,
('leaf', leafName
, before
, self
._treeRoot
),
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']
nodeName
= self
._nodeName
+ ".%d" % (len(self
._nodeNames
) + 1)
if self
._nodeAttrs
.has_key(nodeName
):
msg
= 'Node "%s" already exists.' % nodeName
# Do this early to catch bad <before> spec before creating any items.
beforeIndex
= self
.index(before
, 1)
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)
node
= apply(self
.createcomponent
, ('branch%d'%len(self
._nodeNames
),
attributes
['nodetype'] = 'TreeNode'
node
= apply(self
.createcomponent
, ('leaf%d'%len(self
._nodeNames
),
attributes
['nodetype'] = 'LeafNode'
if len(self
._nodeNames
) == beforeIndex
:
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
)
def delete(self
, *nodes
):
curSel
= self
._treeRoot
.curselection()[0]
name
= self
._nodeNames
.pop(index
)
dnode
= self
._nodeAttrs
[name
]['branch']
del self
._nodeAttrs
[name
]
self
._treeRoot
._unhightlightnode
(dnode
)
for node
in len(self
._nodeNames
):
Pmw
.MegaWidget
.destroy(self
)
def index(self
, index
, forInsert
= 0):
if isinstance(index
, _LeafNode
):
listLength
= len(self
._nodeNames
)
if type(index
) == types
.IntType
:
if forInsert
and index
<= listLength
:
elif not forInsert
and index
< listLength
:
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
raise ValueError, 'TreeNode has no branches'
#elif index is Pmw.SELECT:
# raise ValueError, 'TreeNode has no branches'
# return self._pageNames.index(self.getcurselection())
validValues
= 'a name, a number, Pmw.END, Pmw.SELECT, or a reference to a TreeBrowser Leaf or Branch'
'bad index "%s": must be %s' % (index
, validValues
)
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
# Define the megawidget options.
('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
),
self
.defineoptions(kw
, optiondefs
)
# Initialise the base class (after defining the options).
Pmw
.MegaWidget
.__init
__(self
, parent
)
labelpos
= self
['labelpos']
if self
['label'] == None:
self
._labelWidget
= self
.createcomponent('labelwidget',
#background = self['background'],
#foreground = self['foreground'],
self
._labelWidget
= self
.createcomponent('labelwidget',
label_background
= self
['background'],
label_foreground
= self
['foreground'],
labelmargin
= self
['labelmargin'],
label_text
= self
['label'],
self
._labelWidget
.component('label').bind('<ButtonRelease-1>',
self
._labelWidget
.grid(column
= 1, row
= 0, sticky
= 'w')
self
._labelWidget
.update()
self
._labelheight
= self
._labelWidget
.winfo_height()
self
._lineCanvas
= self
.createcomponent('linecanvas',
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')
self
._lineCanvas
.create_line(cw
/2, 0, cw
/2, ch
/2, tag
='vline')
self
._lineCanvas
.create_line(cw
/2, 0, cw
/2, ch
, tag
='vline')
# Check keywords and initialise options.
return self
._labelWidget
.interior()
def _selectevent(self
, event
):
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']
#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']
def _setlast(self
, last
):
cw
= int(self
._lineCanvas
['width'])
ch
= int(self
._lineCanvas
['height'])
self
._lineCanvas
.create_line(cw
/2, 0, cw
/2, ch
/2, tag
='vline')
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.
('view', 'collapsed', None),
('expandcommand', None, None),
('collapsecommand', None, None),
self
.defineoptions(kw
, optiondefs
)
# Initialise the base class (after defining the options).
apply(_LeafNode
.__init
__,
(self
, parent
, nodeName
, treeRoot
, parentnode
, last
),
_Branching
.__init
__(self
)
# Create the expand/collapse button
self
._viewButton
= self
.createcomponent('viewbutton', (), None,
background
= self
['background'],
width
= self
._labelheight
- 4,
height
= self
._labelheight
- 4,
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
._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
,),
self
.grid_columnconfigure(2,minsize
=0, weight
=1)
#self.grid_rowconfigure(0,minsize=0)
if(self
['view'] == 'expanded'):
Pmw
.drawarrow(self
._viewButton
,
self
._branchFrame
.grid(column
= 2, row
= 1, sticky
='nw')
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
)
Pmw
.drawarrow(self
._viewButton
,
self
._viewButton
.configure(relief
= 'raised')
# Check keywords and initialise options.
def _showbuttonpress(self
, event
):
self
._viewButton
.configure(relief
= 'sunken')
def _toggleview(self
, event
):
self
._viewButton
.configure(relief
= 'sunken')
if(self
['view'] == 'expanded'):
self
._viewButton
.configure(relief
= 'raised')
if(self
['view'] == 'collapsed'):
cmd
= self
['expandcommand']
self
['view'] = 'expanded'
Pmw
.drawarrow(self
._viewButton
,
self
._branchFrame
.grid(column
= 2, row
= 1, sticky
='nw')
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
()
if(self
['view'] == 'expanded'):
cmd
= self
['collapsecommand']
self
['view'] = 'collapsed'
Pmw
.drawarrow(self
._viewButton
,
self
._branchFrame
.grid_forget()
#self._lineCanvas.delete('vline')
self
._lineCanvas
.grid_forget()
self
._parentNode
._sizechange
()
def _setlast(self
, 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')
self
._lineCanvas
.create_line(cw
/2, 1, cw
/2, ch
, tag
='vline')
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.
('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
)
browserFrame
= self
.createcomponent('frame', (), None,
browserFrame
.pack(expand
= 1, fill
='both')
self
._branchFrame
= browserFrame
.interior()
self
._highlightedNode
= None
self
._nodeName
= nodeName
# Check keywords and initialise options.
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
if self
._highlightedNode
!= None:
retVal
= (self
._highlightedNode
,
self
._highlightedNode
._nodeName
,
self
._highlightedNode
['label'])
# 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
if __name__
== '__main__':
rootWin
.title('TreeBrowser Demo')
# Create the hierarchical tree browser widget
treeBrowser
= TreeBrowser(rootWin
,
#selectbackground = "darkgreen",
#selectforeground = 'lightgreen',
selection
= treeBrowser
.curselection()
print "Selected node name:", selection
[1], " label:", selection
[2]
def printdeselected(node
):
selection
= treeBrowser
.curselection()
print "Deselected node name:", selection
[1], " label:", selection
[2]
print "Expanded node name:", node
.getname(), " label:", node
.getlabel()
def printcollapsed(node
):
print "Collapsed node name:", node
.getname(), " label:", node
.getlabel()
# Add a tree node to the top level
treeNodeLevel1
= treeBrowser
.addbranch(label
= 'TreeNode %d'%i,
selectcommand
= printselected
,
deselectcommand
= printdeselected
,
expandcommand
= printexpanded
,
collapsecommand
= printcollapsed
,
# 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
,
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
)
# 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',
command
= treeNodeLevel1
.select
)
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',
treeNodeLevel1
= treeBrowser
.addbranch(selectcommand
= printselected
,
deselectcommand
= printdeselected
,
expandcommand
= printexpanded
,
collapsecommand
= printcollapsed
,
checkButton
= Tkinter
.Checkbutton(treeNodeLevel1
.interior(),
text
= 'Check Button with no label',
command
= treeNodeLevel1
.select
)
treeNodeLevel1
= treeBrowser
.addbranch(label
= 'Label',
selectcommand
= printselected
,
deselectcommand
= printdeselected
,
expandcommand
= printexpanded
,
collapsecommand
= printcollapsed
,
# setup dynamic tree node insertion and removal
self
.dyn
= Tkinter
.IntVar()
self
.dLeaf
= treeBrowser
.addleaf(selectcommand
= self
.dynSelected
,
deselectcommand
= self
.dynDeselected
)
self
.dCheckButton
= Tkinter
.Checkbutton(self
.dLeaf
.interior(),
text
= 'Enable Dynamic Tree',
command
= self
.ChkBtnHandler
)
def dynSelected(self
, node
):
self
.dCheckButton
.configure(background
= self
.dLeaf
.configure('selectbackground')[4])
def dynDeselected(self
, node
):
self
.dCheckButton
.configure(background
= self
.dLeaf
.configure('background')[4])
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
)
dynamicTreeRootNode
.delete(self
.dtree
)
treeBrowser
.pack(expand
= 1, fill
='both')
exitButton
= Tkinter
.Button(rootWin
, text
="Quit", command
=rootWin
.quit
)