📄 tree.py
字号:
self.bind('<Up>', self.prev) # arrow-left/arrow-right self.bind('<Left>', self.ascend) # (hold this down and you expand the entire tree) self.bind('<Right>', self.descend) # home/end self.bind('<Home>', self.first) self.bind('<End>', self.last) # space bar self.bind('<Key-space>', self.toggle) # ----- PRIVATE METHODS (prefixed with "PVT_") ----- # these methods are subject to change, so please try not to use them def PVT_mousefocus(self, event): """Soak up event argument when moused-over""" self.focus_set() # ----- PUBLIC METHODS ----- def tag_bind(self, tag, seq, *args, **kw_args): """Keep track of callback bindings so we can delete them later. I shouldn't have to do this!!!!""" # pass args to superclass func_id=apply(Canvas.tag_bind, (self, tag, seq)+args, kw_args) # save references self.bindings[tag]=self.bindings.get(tag, [])+[(seq, func_id)] def add_list(self, list=None, name=None, id=None, flag=0, expanded_icon=None, collapsed_icon=None): """Add node construction info to list""" n=Struct() n.name=name n.id=id n.flag=flag if collapsed_icon: n.collapsed_icon=collapsed_icon else: if flag: # it's expandable, use closed folder icon n.collapsed_icon=self.collapsed_icon else: # it's not expandable, use regular file icon n.collapsed_icon=self.regular_icon if flag: if expanded_icon: n.expanded_icon=expanded_icon else: n.expanded_icon=self.expanded_icon else: # not expandable, don't need an icon n.expanded_icon=None if list == None: list=[] list.append(n) return list def add_node(self, name=None, id=None, flag=0, expanded_icon=None, collapsed_icon=None): """Add a node during get_contents_callback()""" self.add_list(self.new_nodes, name, id, flag, expanded_icon, collapsed_icon) def find_full_id(self, search): """Search for a node""" return self.root.PVT_find(search) def cursor_node(self, search): """Return node under cursor""" return self.pos def see(self, *items): """Scroll (in a series of nudges) so items are visible""" x1, y1, x2, y2=apply(self.bbox, items) while x2 > self.canvasx(0)+self.winfo_width(): old=self.canvasx(0) self.xview('scroll', 1, 'units') # avoid endless loop if we can't scroll if old == self.canvasx(0): break while y2 > self.canvasy(0)+self.winfo_height(): old=self.canvasy(0) self.yview('scroll', 1, 'units') if old == self.canvasy(0): break # done in this order to ensure upper-left of object is visible while x1 < self.canvasx(0): old=self.canvasx(0) self.xview('scroll', -1, 'units') if old == self.canvasx(0): break while y1 < self.canvasy(0): old=self.canvasy(0) self.yview('scroll', -1, 'units') if old == self.canvasy(0): break def move_cursor(self, node): """Move cursor to node""" self.pos=node x1, y1, x2, y2=self.bbox(node.symbol, node.label) self.coords(self.cursor_box, x1-1, y1-1, x2+1, y2+1) self.see(node.symbol, node.label) def toggle(self, event=None): """Expand/collapse subtree""" self.pos.toggle_state() def next(self, event=None): """Move to next lower visible node""" self.move_cursor(self.pos.next_visible()) def prev(self, event=None): """Move to next higher visible node""" self.move_cursor(self.pos.prev_visible()) def ascend(self, event=None): """Move to immediate parent""" if self.pos.parent_node: # move to parent self.move_cursor(self.pos.parent_node) def descend(self, event=None): """Move right, expanding as we go""" if self.pos.expandable_flag: self.pos.expand() if self.pos.child_nodes: # move to first subnode self.move_cursor(self.pos.child_nodes[0]) return # if no subnodes, move to next sibling self.next() def first(self, event=None): """Go to root node""" # move to root node self.move_cursor(self.root) def last(self, event=None): """Go to last visible node""" # move to bottom-most node self.move_cursor(self.root.PVT_last()) def pageup(self, event=None): """Previous page""" n=self.pos j=self.winfo_height()/self.dist_y for i in range(j-3): n=n.prev_visible() self.yview('scroll', -1, 'pages') self.move_cursor(n) def pagedown(self, event=None): """Next page""" n=self.pos j=self.winfo_height()/self.dist_y for i in range(j-3): n=n.next_visible() self.yview('scroll', 1, 'pages') self.move_cursor(n) # ----- functions for drag'n'drop support ----- def where(self, event): """Determine drag location in canvas coordinates. event.x & event.y don't seem to be what we want.""" # where the corner of the canvas is relative to the screen: x_org=self.winfo_rootx() y_org=self.winfo_rooty() # where the pointer is relative to the canvas widget, # including scrolling x=self.canvasx(event.x_root-x_org) y=self.canvasy(event.y_root-y_org) return x, y def dnd_accept(self, source, event): """Accept dnd messages, i.e. we're a legit drop target, and we do implement d&d functions.""" self.target=None return self def dnd_enter(self, source, event): """Get ready to drag or drag has entered widget (create drag object)""" # this flag lets us know there's been drag motion self.drag=1 x, y=self.where(event) x1, y1, x2, y2=source.widget.bbox(source.symbol, source.label) dx, dy=x2-x1, y2-y1 # create dragging icon if source.expanded_flag: self.dnd_symbol=self.create_image(x, y, image=source.expanded_icon) else: self.dnd_symbol=self.create_image(x, y, image=source.collapsed_icon) self.dnd_label=self.create_text(x+self.text_offset, y, text=source.get_label(), justify='left', anchor='w') def dnd_motion(self, source, event): """Move drag icon""" self.drag=1 x, y=self.where(event) x1, y1, x2, y2=self.bbox(self.dnd_symbol, self.dnd_label) self.move(self.dnd_symbol, x-x1+source.x_off, y-y1+source.y_off) self.move(self.dnd_label, x-x1+source.x_off, y-y1+source.y_off) def dnd_leave(self, source, event): """Finish dragging or drag has left widget (destroy drag object)""" self.delete(self.dnd_symbol) self.delete(self.dnd_label) def dnd_commit(self, source, event): """Object has been dropped here""" # call our own dnd_leave() to clean up self.dnd_leave(source, event) # process pending events to detect target node # update_idletasks() doesn't do the trick if source & target are # on different widgets self.update() if not self.target: # no target node return # we must update data structures based on the drop if self.drop_callback: try: # called with dragged node and target node # this is where a file manager would move the actual file # it must also move the nodes around as it wishes self.drop_callback(source, self.target) except: report_callback_exception()#------------------------------------------------------------------------------# the good 'ol test/demo codeif __name__ == '__main__': import os import sys # default routine to get contents of subtree # supply this for a different type of app # argument is the node object being expanded # should call add_node() def get_contents(node): path=apply(os.path.join, node.full_id()) for filename in os.listdir(path): full=os.path.join(path, filename) name=filename folder=0 if os.path.isdir(full): # it's a directory folder=1 elif not os.path.isfile(full): # but it's not a file name=name+' (special)' if os.path.islink(full): # it's a link name=name+' (link to '+os.readlink(full)+')' node.widget.add_node(name=name, id=filename, flag=folder) root=Tk() root.title(os.path.basename(sys.argv[0])) tree=os.sep if sys.platform == 'win32': # we could call the root "My Computer" and mess with get_contents() # to return "A:", "B:", "C:", ... etc. as it's children, but that # would just be terminally cute and I'd have to shoot myself tree='C:'+os.sep # create the control t=Tree(master=root, root_id=tree, root_label=tree, get_contents_callback=get_contents, width=300) t.grid(row=0, column=0, sticky='nsew') # make expandable root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) # add scrollbars sb=Scrollbar(root) sb.grid(row=0, column=1, sticky='ns') t.configure(yscrollcommand=sb.set) sb.configure(command=t.yview) sb=Scrollbar(root, orient=HORIZONTAL) sb.grid(row=1, column=0, sticky='ew') t.configure(xscrollcommand=sb.set) sb.configure(command=t.xview) # must get focus so keys work for demo t.focus_set() # we could do without this, but it's nice and friendly to have Button(root, text='Quit', command=root.quit).grid(row=2, column=0, columnspan=2) # expand out the root t.root.expand() root.mainloop()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -