News

[17/Sep/2009:19:20] Release 0.5 includes Open Flash Chart support.

[01/Jul/2009:10:50] Repoze.who authentication tutorial added

[22/Jun/2009:11:36] AJAX calculator tutorial added.

[01/May/2009:14:15] MVC/SQL based wiki tutorial added.



Contact Email:



view source
W1100_2200.
TreeView.
whiff



Download instructions
at whiff.sourceforge.net
project page
.
WHIFF DOCUMENTION

Using tree view widgets

This tutorial illustrates how to add a "tree view" widget to a web page.

Contents:
A simple navigation tree view
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
It 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.

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.  

×
This advanced tutorial assumes the reader has significant experience with data structures, Python, and web technology.

A simple navigation tree view

The tree view in the embedded frame below presents a tree widget implemented by 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.
For simplicity the tree view uses the default formatting options. Please see below for information about how to change the tree formatting.

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}}
The specification passes two arguments to the 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 ... ]
}
The descendent nodes in the 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"
  }
Each node must have a unique 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>"

Using a resource to get external tree data

Trees are often used to organize very large collections of data. When tree representations are large it is not feasible to embed the entire tree directly in a page template. A tree data source may be implemented as a WHIFF resource to allow WHIFF tree views to present very large (or even infinite) tree structures.

Configuring a tree information resource for an application root

A resource for a tree view should be installed in a root directory application containing the pages which need the resource. A resource for a tree view must present the tree structure using a "flattened" tree representation which acts like a Python dictionary.

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)
....
The 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 }
where the 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).

Using the resource in a tree widget on a page.

The WHIFF distribution provides a number of middlewares which present tree view widgets using tree resources: 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}}
The 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:

A more realistic example: a tree view for a directory

Other tree resources cannot be represented directly by Python dictionaries. For example the 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
The 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': '.'
}
>>> 
>>>
As illustrated above, a 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.

Configuring the directory tree viewer with modified HTML layout

The application configuration script 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()
In addition to installing the ["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>"
    }
These environment entries modify the formatting of the tree view as HTML. Please consult the source for whiff/middleware/treeView.py to see how configuration parameters like these are used and which are available for your use.

Presenting a tree view of a directory hierarchy

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>
The configuration script may be run as a program to display any visible directory as follows:
INSTALL/demo/dirTree/$ python runDir.py /var/hg/repos/
directory tree server
serving wsgi at 8888
Pointing a browser at http://localhost:8888/index displays a tree view that looks something like this
A publicly viewable instance of this application is available at http://aaron.oirt.rutgers.edu/myapp/DirectoryTree/index -- please have a look!

A fancier example

The demo subtree 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.

Conclusion

This tutorial showed how to present large and small collections of information on web pages using WHIFF tree views. Tree views provide a powerful paradigm for organizing and browsing complex hierarchies of information.
0 comments.
Care to comment?
name: (required)
- email (not published):
comment: (required)

<< security number? >>