📄 tree.py
字号:
# Highly optimized Tkinter tree control# by Charles E. "Gene" Cash## This is documented more fully on my homepage at# http://home.cfl.rr.com/genecash/ and if it's not there, look in the Vaults# of Parnassus at http://www.vex.net/parnassus/ which I promise to keep# updated.## Thanks to Laurent Claustre <claustre@esrf.fr> for sending lots of helpful# bug reports.## This copyright license is intended to be similar to the FreeBSD license. ## Copyright 1998 Gene Cash All rights reserved. ## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are# met:## 1. Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.# 2. Redistributions in binary form must reproduce the above copyright# notice, this list of conditions and the following disclaimer in the# documentation and/or other materials provided with the# distribution.## THIS SOFTWARE IS PROVIDED BY GENE CASH ``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 REGENTS OR CONTRIBUTORS 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.## This means you may do anything you want with this code, except claim you# wrote it. Also, if it breaks you get to keep both pieces.## 02-DEC-98 Started writing code.# 22-NOV-99 Changed garbage collection to a better algorithm.# 28-AUG-01 Added logic to deal with exceptions in user callbacks.# 02-SEP-01 Fixed hang when closing last node.# 07-SEP-01 Added binding tracking so nodes got garbage-collected.# Also fixed subclass call to initialize Canvas to properly deal# with variable arguments and keyword arguments.# 11-SEP-01 Bugfix for unbinding code.# 13-OCT-01 Added delete & insert methods for nodes (by email request).# LOTS of code cleanup.# Changed leading double underscores to PVT nomenclature.# Added ability to pass Node subclass to Tree constructor.# Removed after_callback since subclassing Node is better idea.# 15-OCT-01 Finally added drag'n'drop support. It consisted of a simple# change to the Node PVT_click method, and addition of logic like# the example in Tkdnd.py. It took 3 days to grok the Tkdnd# example and 2 hours to make the code changes. Plus another 1/2# day to get a working where() function.# 16-OCT-01 Incorporated fixes to delete() and dnd_commit() bugs by# Laurent Claustre <claustre@esrf.fr>.# 17-OCT-01 Added find_full_id() and cursor_node() methods.# 18-OCT-01 Fixes to delete() on root during collapse and with# drag-in-progress flag by Laurent Claustre <claustre@esrf.fr>.# 10-FEB-02 Fix to prev_visible() by Nicolas Pascal <pascal@esrf.fr>.# Fixes which made insert_before()/insert_after() actually work.# Also added expand/collapse indicators like Internet Explorer# as requested by Nicolas.# 11-FEB-02 Another fix to prev_visible(). It works this time. Honest.# 31-MAY-02 Added documentation strings so the new PYthon 2.2 help function# is a little more useful.# 19-AUG-02 Minor fix to eliminate crash in "treedemo-icons.py" caused by# referencing expand/collapse indicators when lines are turned off.# 15-OCT-02 Used new idiom for calling Canvas superclass.# 18-NOV-02 Fixed bug discovered by Amanjit Gill <amanjit.gill@gmx.de>, where# I didn't pass "master" properly to the Canvas superclass. Sigh.# One step forward, one step back.import Tkdndfrom Tkinter import *#------------------------------------------------------------------------------def report_callback_exception(): """report exception on sys.stderr.""" import traceback import sys sys.stderr.write("Exception in Tree control callback\n") traceback.print_exc() #------------------------------------------------------------------------------class Struct: """Helper object for add_node() method""" def __init__(self): pass#------------------------------------------------------------------------------class Node: """Tree helper class that's instantiated for each element in the tree. It has several useful attributes: parent_node - immediate parent node id - id assigned at creation expanded_icon - image displayed when folder is expanded to display children collapsed_icon - image displayed when node is not a folder or folder is collapsed. parent_widget - reference to tree widget that contains node. expandable_flag - is true when node is a folder that may be expanded or collapsed. expanded_flag - true to indicate node is currently expanded. h_line - canvas line to left of node image. v_line - canvas line below node image that connects children. indic - expand/collapse canvas image. label - canvas text label symbol - current canvas image Please note that methods prefixed PVT_* are not meant to be used by client programs.""" def __init__(self, parent_node, id, collapsed_icon, x, y, parent_widget=None, expanded_icon=None, label=None, expandable_flag=0): """Create node and initialize it. This also displays the node at the given position on the canvas, and binds mouseclicks.""" # immediate parent node self.parent_node=parent_node # internal name used to manipulate things self.id=id # bitmaps to be displayed self.expanded_icon=expanded_icon self.collapsed_icon=collapsed_icon # tree widget we belong to if parent_widget: self.widget=parent_widget else: self.widget=parent_node.widget # for speed sw=self.widget # our list of child nodes self.child_nodes=[] # flag that node can be expanded self.expandable_flag=expandable_flag self.expanded_flag=0 # add line if parent_node and sw.line_flag: self.h_line=sw.create_line(x, y, x-sw.dist_x, y) else: self.h_line=None self.v_line=None # draw approprate image self.symbol=sw.create_image(x, y, image=self.collapsed_icon) # add expand/collapse indicator self.indic=None if expandable_flag and sw.line_flag and sw.plus_icon and sw.minus_icon: self.indic=sw.create_image(x-sw.dist_x, y, image=sw.plus_icon) # add label self.label=sw.create_text(x+sw.text_offset, y, text=label, anchor='w') # single-click to expand/collapse if self.indic: sw.tag_bind(self.indic, '<1>', self.PVT_click) else: sw.tag_bind(self.symbol, '<1>', self.PVT_click) # for drag'n'drop target detection sw.tag_bind(self.symbol, '<Any-Enter>', self.PVT_enter) sw.tag_bind(self.label, '<Any-Enter>', self.PVT_enter) # for testing (gotta make sure nodes get properly GC'ed) #def __del__(self): # print self.full_id(), 'deleted' # ----- PUBLIC METHODS ----- def set_collapsed_icon(self, icon): """Set node's collapsed image""" self.collapsed_icon=icon if not self.expanded_flag: self.widget.itemconfig(self.symbol, image=icon) def set_expanded_icon(self, icon): """Set node's expanded image""" self.expanded_icon=icon if self.expanded_flag: self.widget.itemconfig(self.symbol, image=icon) def parent(self): """Return node's parent node""" return self.parent_node def prev_sib(self): """Return node's previous sibling (the child immediately above it)""" i=self.parent_node.child_nodes.index(self)-1 if i >= 0: return self.parent_node.child_nodes[i] else: return None def next_sib(self): """Return node's next sibling (the child immediately below it)""" i=self.parent_node.child_nodes.index(self)+1 if i < len(self.parent_node.child_nodes): return self.parent_node.child_nodes[i] else: return None def next_visible(self): """Return next lower visible node""" n=self if n.child_nodes: # if you can go right, do so return n.child_nodes[0] while n.parent_node: # move to next sibling i=n.parent_node.child_nodes.index(n)+1 if i < len(n.parent_node.child_nodes): return n.parent_node.child_nodes[i] # if no siblings, move to parent's sibling n=n.parent_node # we're at bottom return self def prev_visible(self): """Return next higher visible node""" n=self if n.parent_node: i=n.parent_node.child_nodes.index(n)-1 if i < 0: return n.parent_node else: j=n.parent_node.child_nodes[i] return j.PVT_last() else: return n def children(self): """Return list of node's children""" return self.child_nodes[:] def get_label(self): """Return string containing text of current label""" return self.widget.itemcget(self.label, 'text') def set_label(self, label): """Set current text label""" self.widget.itemconfig(self.label, text=label) def expanded(self): """Returns true if node is currently expanded, false otherwise""" return self.expanded_flag def expandable(self): """Returns true if node can be expanded (i.e. if it's a folder)""" return self.expandable_flag def full_id(self): """Return list of IDs of all parents and node ID""" if self.parent_node: return self.parent_node.full_id()+(self.id,) else: return (self.id,) def expand(self): """Expand node if possible""" if not self.expanded_flag: self.PVT_set_state(1) def collapse(self): """Collapse node if possible""" if self.expanded_flag: self.PVT_set_state(0) def delete(self, me_too=1): """Delete node from tree. ("me_too" is a hack not to be used by external code, please!)""" sw=self.widget if not self.parent_node and me_too: # can't delete the root node raise ValueError, "can't delete root node" self.PVT_delete_subtree() # move everything up so that distance to next subnode is correct n=self.next_visible() x1, y1=sw.coords(self.symbol) x2, y2=sw.coords(n.symbol) if me_too: dist=y2-y1 else: dist=y2-y1-sw.dist_y self.PVT_tag_move(-dist) n=self if me_too: if sw.pos == self: # move cursor if it points to current node sw.move_cursor(self.parent_node) self.PVT_unbind_all() sw.delete(self.symbol) sw.delete(self.label) sw.delete(self.h_line) sw.delete(self.v_line) sw.delete(self.indic) self.parent_node.child_nodes.remove(self) # break circular ref now, so parent may be GC'ed later n=self.parent_node self.parent_node=None n.PVT_cleanup_lines() n.PVT_update_scrollregion()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -