
A simple navigation tree viewIt is often convenient to organize information into tree structures. For example books are often organized into chapters which have multiple levels of sections within each chapter, and computer file systems are organized into directories which contain many levels of sub-directories as well as data files.
Using a resource to get external tree data
Configuring a tree information resource for an application root
Using the resource in a tree widget on a page.
A more realistic example: a tree view for a directory
Configuring the directory tree viewer with modified HTML layout
Presenting a tree view of a directory hierarchy
A fancier example
Conclusion
Interactive tree view widgets allow users to see information organized as a tree, often allowing the user to explore only the parts of the tree of interest, and ignore the parts of the tree that are not relevant to the user's present purpose.
For example the WHIFF distribution includes a demo treeview implementation for the GenBank cladogram of biological relatedness among about a million species of life forms. The publicly viewable GenBank demo is available at http://aaron.oirt.rutgers.edu/myapp/GenBankTree/index. Below is a screenshot of the cladogram demo:
This tutorial explains some of the support for presenting tree views that is built into the WHIFF distribution, both using small trees directly embedded in page templates, and using large externally stored tree structures.
frameTree.whiff
which might be used for document navigation among a small number of documents.
Please click the + and - flags to see the tree
expand and contract.
A tree view like the one shown above might be used in a navigation frame
for a web page hierarchy which also includes a "main text" frame.
Here clicking on the +/- flags would expand
or collapse the tree to show the navigation options and clicking on the
other links of the tree might load a new document into the "main text"
frame.
The whiff_middleware/BasicTreeView makes it easy to implement
navigation frames of this sort -- the frame implementation need only specify
the navigation tree as a JSON data structure and the name of the node
in the tree of greatest interest.
The frameTree.whiff configuration template implements
the frameTree navigation page using
whiff_middleware/BasicTreeView as follows:
{{env whiff.content_type: "text/html",
whiff.parse_cgi: true,
whiff.strip: true
/}}
{{include "whiff_middleware/BasicTreeView"}}
{{using focus}} science {{/using}}
{{using tree}}
{ "id": "general",
"body": "<a href='http:www.wikipedia.com'>general studies</a>",
"children": [
{ "id" : "liberal arts",
"body" : "<a href='http://www.bmcc.cuny.edu/liberalarts/' target='txt'>liberal arts: the study of humanities</a>",
"children": [
{ "id" : "literature",
"body" : "literature: the study of writings"
},
{ "id" : "ethics",
"body" : "ethics: the study of right and wrong"
}
]
},
{ "id" : "fine arts",
"body" : "<a href='http://www.fineartlamps.com/' target='txt'>fine arts: the study of beauty and creativity</a>",
"children": [
{ "id" : "sculpture",
"body" : "sculpture: the study of artistic expression in three dimensional forms"
},
{ "id" : "music",
"body" : "music: the study of artistic expression using sounds"
}
]
},
{ "id" : "science",
"body" : "<a href='http://www.scientificamerican.com' target='txt'>science: the study of the physical properties of things</a>",
"children": [
{ "id" : "physics",
"body" : "physics: the study of the behaviour and interaction of non-living objects"
},
{ "id" : "biology",
"body" : "<a href='http://www.profishservices.com/Bio.htm' target='txt'>biology: the study of living things</a>"
}
]
},
{ "id" : "engineering",
"body" : "<a href='http://www.menloeng.com/' target='txt'>engineering: the study of how to make things work.</a>",
"children": [
{ "id" : "civil engineering",
"body" : "civil engineering: the study of how to build civic infrastructures"
},
{ "id" : "electrical engineering",
"body" : "electrical engineering: the study of the use of electricity"
}
]
}
]
}
{{/using}}
{{/include}}
whiff_middleware/BasicTreeView middleware:
The focus argument specifies that the initial view of the tree should be focussed on the science
node of the tree, and the tree argument specifies the data for the tree to present, represented as
a JSON data structure.
The tree is built from mappings representing nodes where the top level node looks like this:
{ "id": "general",
"body": "<a href='http:www.wikipedia.com'>general studies</a>",
"children": [ ... CHILDREN NODES ... ]
}
tree are similar, except that the leaf nodes
(which have no descendents) may omit the children entry:
{ "id" : "sculpture",
"body" : "sculpture: the study of artistic expression in three dimensional forms"
}
id entry which is used to identify that node, and a body
entry which is used as the data to present when that node is visible. In the case of a navigation
frame the body will probably be implemented using a link or other interactive element to allow the
user to select the node, such as:
"body" : "<a href='http://www.profishservices.com/Bio.htm' target='txt'>biology: the study of living things</a>"
For the purposes of illustration the WHIFF documentation directory
is configured using a Python dictionary to represent a tree.
The configuration script INSTALL/doc/servedocs.py configures the
documentation application using the following declarations:
....
# example tree data structure for tree views
TREE_EXAMPLE = {
"focus":
{ "body": "this is ME, mom<br>next line<br>third line",
"parent": "parent",
"children": ["child1", "child2"]
},
"parent":
{ "body": "this is the gramma<br>next line<br>third line",
"children": ["sibling", "focus"]
},
"child1":
{ "body": "this is the daughter<br>next line<br>third line",
"parent": "focus"
},
"child2":
{ "body": "this is the son<br>next line<br>third line",
"parent": "focus"
},
"cousin":
{ "body": "this is the cousin<br>next line<br>third line",
"parent": "sibling"
},
"sibling":
{ "body": "this is the uncle<br>next line<br>third line",
"parent": "parent",
"children": ["cousin"]
}
}
# the root directory application for documentation
whiffDocumentation = resolver.moduleRootApplication(...)
# install resources in the whiffDocumentation
whiffDocumentation.registerStaticResource(prefix="TREE_EXAMPLE",
resourceValue=TREE_EXAMPLE)
....
TREE_EXAMPLE dictionary is tiny, but it illustrates
the general method for adding a dictionary-like interface for representing
a tree of any size.
The small TREE_EXAMPLE dictionary
tree resource provides a dictionary-like interface because
it is a Python dictionary ;c). Other tree resource implementations
need not be dictionaries, but they must be dictionary like objects
which
must at least implement a tree.__getitem__(name) method
which returns a flattened node representation for the node with the
given name, or which raises a KeyError if there is no
such node.
The tree resource indexes flattened tree node representations by node name. The flattened tree node entries indexed by the tree resource are of the following form
{ "body" : BODY_TEXT, "parent": NAME_OF_PARENT, "children": LIST_OF_NAMES_OF_CHILDREN }
body is required, the parent may be omitted only for
the root of the tree (which has no parent), and the children may be omitted
for leaves of the tree (which have no descendants).
whiff_middleware/treeViewAjax presents a tree view which
does not reload the page when the tree changes, and
whiff_middleware/treeViewFrame presents a tree view which
reloads the page containing the tree whenever the tree changes (for
use in navigation frames, for example).
The configuration template ajaxTree.whiff listed below
uses the whiff_middleware/treeViewAjax middleware to present
a tree view using the ["TREE_EXAMPLE"] resource.
{{env whiff.content_type: "text/html",
whiff.parse_cgi: true,
whiff.strip: true
/}}
{{include "whiff_middleware/treeViewAjax"}}
{{using focusNode}} focus {{/using}}
{{using infoPath}} ["TREE_EXAMPLE"] {{/using}}
{{/include}}
focusNode argument identifies the initial node of interest
and the infoPath argument provides the path to the tree data resource.
The frame embedded below shows the ajaxTree page in action:
INSTALL/demo/dirTree/.. demo subtree implements a tree view which
examines a directory hierarchy in a file system tree.
The fileSystemResource.py Python module implements the
DirectoryTreeDictionary wrapper class
which
makes a file system tree look like a Python dictionary.
import os
import urllib
class DirectoryTreeDictionary:
def __init__(self, rootDirectory):
self.rootDirectory = os.path.abspath(rootDirectory)
#pr "root directory is", self.rootDirectory
def __getitem__(self, path):
#pr "getting info dict for", repr(path)
assert path.find("..")<0, "relative paths are not permitted"
while path.startswith("/"):
path = path[1:]
rootDirectory = self.rootDirectory
fullpath = os.path.join(rootDirectory, path)
#pr "full path for", path, "is", fullpath
if not os.path.exists(fullpath):
raise KeyError, "no such file "+repr(path)
D = {}
parent = children = None
(parent, dummy) = os.path.split(path)
if parent==path or not parent:
parent = None
try:
if os.path.isdir(fullpath):
listing = os.listdir(fullpath)
nlisting = len(listing)
body = "%s: dir with %s entries" % (path, nlisting)
children = [ os.path.join(path, p) for p in listing ]
#pr "for", (path, fullpath), "children are", children
elif os.path.isfile(fullpath):
size = os.path.getsize(fullpath)
anchor = "getContent?path=%s" % (urllib.quote(path))
link = '<a href="%s">%s</a>' % (anchor, path)
body = "%s: file of size %s" % (link, size)
except OSError:
body = "%s: protected file" % path
D["body"] = body
if parent:
D["parent"] = parent
if children:
D["children"] = children
#pr "returning info dict", (path, D)
return D
DirectoryTreeDictionary class presents flattened tree node
representations for files and sub-directories below a root directory. We may
test the DirectoryTreeDictionary class using the Python interactive
prompt (I added white space below to make the output more readable):
>>> from fileSystemResource import *
>>> D = DirectoryTreeDictionary("/tmp")
>>> D["."]
{
'body': '.: dir with 5 entries',
'children': [
'./.Python',
'./apache.start',
'./apache.stop',
'./bin',
'./dmp'
]
}
>>> D["./bin"]
{'body': './bin: dir with 1 entries',
'children': ['./bin/python2.5'],
'parent': '.'
}
>>> D['./apache.stop']
{'body': '<a href="getContent?path=./apache.stop">./apache.stop</a>: file of size 0',
'parent': '.'
}
>>>
>>>
DirectoryTreeDictionary instance provides a dictionary
interface which maps partial file paths to flattened dictionary node representations.
In the representations "regular files" or directories with no content show up as leaves
and directories with content show up as internal nodes where the children represent the
directory listing.
runDir.py configures a root with a
resource for an instance of DirectoryTreeDictionary
at the resource path ["tree"]:
from whiff import resolver
from whiff.middleware import displayTraceback
import fileSystemResource
env = {
'treeView.bodyPrefix': "<div style=\"border-left:3px solid green; padding-left:10px\">",
'treeView.bodySuffix': "</div>",
'treeView.tailPrefix': "<div style=\"border-left:3px solid yellow; padding-left:10px\">",
'treeView.tailSuffix': "</div>"
}
def directoryTreeApplication(rootDirectory):
import showDir
app = resolver.moduleRootApplication("/", showDir,
#exception_middleware=None,
exception_middleware=displayTraceback.__middleware__,
on_not_found=None, # show traceback (could comment)
environment = env,
)
tree = fileSystemResource.DirectoryTreeDictionary(rootDirectory)
app.registerStaticResource(prefix="tree",
resourceValue=tree)
app.registerStaticResource(prefix="root",
resourceValue=rootDirectory)
return app
if __name__=="__main__":
import wsgiref.simple_server
import sys
import os
rootdir = ".."
if len(sys.argv)>1:
rootdir = sys.argv[1]
rootdir = os.path.abspath(rootdir)
testapp = directoryTreeApplication(rootdir)
print "directory tree server"
print "serving wsgi at 8888"
srv = wsgiref.simple_server.make_server('localhost', 8888, testapp)
srv.serve_forever()
["tree"] resource
runDir.py also adds WHIFF environment entries for the root application
env = {
'treeView.bodyPrefix': "<div style=\"border-left:3px solid green; padding-left:10px\">",
'treeView.bodySuffix': "</div>",
'treeView.tailPrefix': "<div style=\"border-left:3px solid yellow; padding-left:10px\">",
'treeView.tailSuffix': "</div>"
}
whiff/middleware/treeView.py to see how configuration parameters
like these are used and which are available for your use.
The tree view is presented in the configuration file showDir/index.whiff
{{env whiff.content_type: "text/html"
/}}
<html>
<head>
<title>
Directory tree browser for {{include "whiff_middleware/getResource"}} ["root"] {{/include}}
</title>
</head>
<body style="font-family:Verdana, Arial, Helvetica, sans-serif;background-color: eeeeaa;color: 3311ff;">
<h1>
Directory tree browser for {{include "whiff_middleware/getResource"}} ["root"] {{/include}}
</h1>
This demo provides a dynamic searchable tree presenting
a view of a file system directory contents.
<p>
The code that implements this demo is available
as part of the
<a href="http://whiff.sourceforge.net">WHIFF package</a>.
<br>
{{include "whiff_middleware/treeViewAjax"}}
{{using focusNode}} / {{/using}}
{{using infoPath}} ["tree"] {{/using}}
{{/include}}
</body>
INSTALL/demo/dirTree/$ python runDir.py /var/hg/repos/
directory tree server
serving wsgi at 8888
http://localhost:8888/index displays a tree view
that looks something like this
INSTALL/demo/tree
provides an even more sophisticated example of a tree view which presents
a view of a tree with about a million entries -- the GenBank biological cladogram
database. The GenBank demo also illustrates how to use images as node flags.
Please go
http://aaron.oirt.rutgers.edu/myapp/GenBankTree/index
to see the GenBank tree in action and please examine the source under
INSTALL/demo/tree to see how the demo is put together.