################################################################################ # # This file is part of Gato (Graph Animation Toolbox) # # file: GatoFile.py # author: Achim Gaedke (achim.gaedke@zpr.uni-koeln.de) # # Copyright (C) 1998-2005, Alexander Schliep, Winfried Hochstaettler and # Copyright 1998-2001 ZAIK/ZPR, Universitaet zu Koeln # # Contact: schliep@molgen.mpg.de, wh@zpr.uni-koeln.de # Information: http://gato.sf.net # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # This file is version $Revision: 1.8 $ # from $Date: 2005/02/22 11:13:20 $ # last change by $Author: schliep $. # ################################################################################ import Tkinter import sys import os import os.path import codecs import xml.dom import xml.dom.minidom class Node: """ base class for all elements of my tree, provides icon support, selection features """ def __init__(self,parent=None, name=None, icon=None, anchor=(0,0)): """ initialises the node """ # create the default image once.... self.setParent(parent) self.name=name self.icon=icon self.anchor=anchor self.selectable=0 self.selected=0 self.selectionItem=None # cached item tags self.canvas=self.nameItem=self.iconItem=None def isVisible(self): """ ask, if this node is displayed """ if self.canvas: return 1 else: return 0 def display(self,recursive=0): """ display this node, all components are displayed or reconfigured if recursive is 0, the tail of the tree will be reordered """ self.canvas=self.parent.getCanvas() # the parent is not displayed, so we do not display ourself if self.canvas is None: return nx0=nx1=ix0=ix1=self.anchor[0] ny0=ny1=iy0=iy1=self.anchor[1] # display or update icon if self.icon: if not self.iconItem: self.iconItem=self.canvas.create_image(self.anchor, anchor=Tkinter.NW, image=self.icon) else: self.canvas.itemconfigure(self.iconItem,image=self.icon) (ix0,iy0,ix1,iy1)=self.canvas.bbox(self.iconItem) self.canvas.tag_bind(self.iconItem,"",self.selectionCallback) else: if self.iconItem: self.canvas.tag_unbind(self.iconItem) self.canvas.delete(self.iconItem) self.iconItem=None # display or update text after icon... if self.name: if not self.nameItem: self.nameItem=self.canvas.create_text((ix1+1,self.anchor[1]), anchor=Tkinter.NW, text=self.name) else: self.canvas.itemconfigure(self.nameItem, text=self.name) (nx0,ny0,nx1,ny1)=self.canvas.bbox(self.nameItem) self.canvas.tag_bind(self.nameItem,"",self.selectionCallback) else: if self.nameItem: self.canvas.tag_unbind(self.nameItem) self.canvas.delete(self.nameItem) self.nameItem=None # look whether icon is taller than text diff=iy1-iy0-ny1+ny0 if diff>0 and self.nameItem: # center text self.canvas.coords(self.nameItem,(nx0,(iy0+iy1)/2)) self.canvas.itemconfigure(self.nameItem,anchor=Tkinter.W) elif diff<0 and self.iconItem: # center icon self.canvas.coords(self.iconItem,(ix0,(ny0+ny1)/2)) self.canvas.itemconfigure(self.iconItem,anchor=Tkinter.W) # maintain selection if self.selected: coords=apply(self.canvas.bbox, filter(None,[self.iconItem,self.nameItem])) if self.selectionItem: self.canvas.coords(self.selectionItem,coords) else: self.selectionItem=self.canvas.create_rectangle(coords,outline="",fill="green") self.canvas.lower(self.selectionItem) else: if self.selectionItem: self.canvas.delete(self.selectionItem) self.selectionItem=None # only top call should reorder the tree if recursive==0: self.parent.moveAfterChild(self) def update(self,recursive=0): """ update the icon and name """ if self.isVisible(): return self.display(recursive) else: return None def conceal(self,recursive=0): """ conceal this node from tree, destruct all icons and text if recursive is 0, the tail of the tree will be reordered """ # we are not displayed, do nothing if not self.canvas: return if self.nameItem: self.canvas.tag_unbind(self.nameItem,"") self.canvas.delete(self.nameItem) self.nameItem=None if self.iconItem: self.canvas.tag_unbind(self.iconItem,"") self.canvas.delete(self.iconItem) self.iconItem=None if self.selectionItem: self.canvas.delete(self.selectionItem) self.selectionItem=None self.canvas=None # only top call should reorder the tree if recursive==0: self.parent.moveAfterChild(self) def getBoundingBox(self): # we are not displayed if self.canvas is None: return None items=self.getAllItems() if items: return apply(self.canvas.bbox,items) else: return (self.anchor[0],self.anchor[1],self.anchor[0],self.anchor[1]) def getAllItems(self): items=[] if self.nameItem: items.append(self.nameItem) if self.iconItem: items.append(self.iconItem) if self.selectionItem: items.append(self.selectionItem) return items def getNamePath(self): parentPath=self.parent.getNamePath() parentPath.append(self.name) return parentPath def getCanvas(self): """ get canvas from parent """ return self.canvas def setParent(self,newParent): """ sets own parent """ self.parent=newParent def select(self): """ show selection highligting """ self.selected=1 if self.canvas: coords=apply(self.canvas.bbox, filter(None,[self.iconItem,self.nameItem])) if self.selectionItem: self.canvas.coords(self.selectionItem,coords) else: self.selectionItem=self.canvas.create_rectangle(coords,outline="",fill="green") self.canvas.lower(self.selectionItem) def deselect(self): self.selected=0 if self.canvas and self.selectionItem: self.canvas.delete(self.selectionItem) self.selectionItem=None def selectionCallback(self,event): """ """ if not self.selectable: return if not self.selected: self.select() else: self.deselect() def printNode(self,indent=""): refcnt=sys.getrefcount(self) print "%s%s:%d"%(indent,self.name,refcnt) class Leaf(Node): """ the leaf cannot contain children. """ defaultIconData="R0lGODlhDAAMAKEAALLA3AAAAP//8wAAACH5BAEAAAAALAAAAAAMAAwAAAIgRI4Ha+IfWHsOrSASvJTGhnhcV3EJlo3kh53ltF5nAhQAOw==" def __init__(self,parent=None, anchor=(0,0), name=None, icon=None): if not Leaf.__dict__.has_key("defaultIcon"): Leaf.defaultIcon=Tkinter.PhotoImage(data=Leaf.defaultIconData) if name==None: raise Exception("name should be text") if not icon: icon=Leaf.defaultIcon Node.__init__(self,parent=parent, name=name, anchor=anchor, icon=icon) class Branch(Node): """ the branch contains leafs or branches it can be expanded and collapsed and maintains its children """ expandData=""" #define plus_width 13 #define plus_height 13 static unsigned char plus_bits[] = { 0x00, 0x00, 0xfe, 0x0f, 0x02, 0x08, 0x42, 0x08, 0x42, 0x08, 0x42, 0x08, 0xfa, 0x0b, 0x42, 0x08, 0x42, 0x08, 0x42, 0x08, 0x02, 0x08, 0xfe, 0x0f, 0x00, 0x00};""" collapseData=""" #define minus_width 13 #define minus_height 13 static unsigned char minus_bits[] = { 0x00, 0x00, 0xfe, 0x0f, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0xfa, 0x0b, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0xfe, 0x0f, 0x00, 0x00}; """ def __init__(self,parent=None, anchor=(0,0), name=None, expanded=0, children=[]): """ initialises a branch: children can be specified... """ # initialise Icon Data if not Branch.__dict__.has_key("expandImage"): Branch.expandImage=Tkinter.BitmapImage(data=Branch.expandData) if not Branch.__dict__.has_key("collapseImage"): Branch.collapseImage=Tkinter.BitmapImage(data=Branch.collapseData) self.expanded=expanded icon=Branch.expandImage if self.expanded: icon=self.collapseImage self.children=children Node.__init__(self, parent=parent, anchor=anchor, name=name, icon=icon) def expand(self): """ Special feature of a branch: can display all direct children before expanding the childlist is updated """ self.expanded=1 self.update() def collapse(self): """ Special feature of a branch: can conceal all children on demand after collapsing, the childlist is cleaned up """ self.expanded=0 self.update() def updateChildList(self): """ called before expanding the branch in order to update all necessary children """ pass def cleanupChildList(self): """ called after collapsed the branch in order to destruct all unused children """ pass def display(self,recursive=0): """ set expand/collapse handle, care about children and return.... """ self.canvas=self.parent.getCanvas() # parent is not displayed if self.canvas is None: return if self.expanded: self.icon=self.collapseImage Node.display(self,recursive+1) (x0,y0,x1,y1)=apply(self.canvas.bbox,filter(None,[self.nameItem,self.iconItem])) (ix0,iy0,ix1,iy1)=self.canvas.bbox(self.iconItem) self.updateChildList() for child in self.children: child.anchor=(ix1+1,y1+1) child.display(recursive+1) (x0,y0,x1,y1)=child.getBoundingBox() else: self.icon=self.expandImage Node.display(self,recursive+1) for child in self.children: child.conceal(recursive+1) self.cleanupChildList() # now take care about the rest... self.canvas.tag_bind(self.iconItem,"", self.toogleCallback) if recursive==0: self.parent.moveAfterChild(self) def conceal(self,recursive): """ conceal all children and self """ for child in self.children: child.conceal(recursive+1) self.cleanupChildList() Node.conceal(self,recursive) def getAllItems(self): """ get all canvas items, that contribute to the displayed branch """ items=Node.getAllItems(self) for child in self.children: items+=child.getAllItems() return items def moveAfterChild(self, child): """ recursive move mechanism in order to replace all children beneath the calling child """ if not self.isVisible(): self.parent.moveAfterChild(self) return # look for calling child position idx=self.children.index(child) # if this was the last one... if idx>=len(self.children): self.parent.moveAfterChild(self) return # search for precedent visible child lastIdx=idx while lastIdx>=0 and not self.children[lastIdx].isVisible(): lastIdx-=1 if lastIdx<0: self.parent.moveAfterChild(self) return # determine new position (nx1,ny1,nx2,ny2)=self.children[lastIdx].getBoundingBox() # look for child after calling child # and get succeding tags to move.. idx+=1 items=[] while idx