/opt/imh/python3.13/lib/python3.13/idlelib
NameSizeModeActions
Icons/-0755rm
__pycache__/-0755rm
autocomplete.py93540644editdlrm
autocomplete_w.py208630644editdlrm
autoexpand.py32160644editdlrm
browser.py85880644editdlrm
calltip.py72670644editdlrm
calltip_w.py70830644editdlrm
ChangeLog563600644editdlrm
codecontext.py114200644editdlrm
colorizer.py147830644editdlrm
config-extensions.def22660644editdlrm
config-highlight.def28640644editdlrm
config-keys.def109100644editdlrm
config-main.def31680644editdlrm
config.py384030644editdlrm
configdialog.py1053030644editdlrm
config_key.py152300644editdlrm
CREDITS.txt21480644editdlrm
debugger.py209970644editdlrm
debugger_r.py121150644editdlrm
debugobj.py41770644editdlrm
debugobj_r.py10820644editdlrm
delegator.py10440644editdlrm
dynoption.py19930644editdlrm
editor.py695570644editdlrm
extend.txt36320644editdlrm
filelist.py38710644editdlrm
format.py157770644editdlrm
grep.py75210644editdlrm
help.html605660644editdlrm
help.py118430644editdlrm
help_about.py90230644editdlrm
history.py40650644editdlrm
HISTORY.txt103130644editdlrm
hyperparser.py128890644editdlrm
idle.bat1770644editdlrm
idle.py4540644editdlrm
idle.pyw5700644editdlrm
iomenu.py161590644editdlrm
macosx.py92900644editdlrm
mainmenu.py39380644editdlrm
multicall.py186520644editdlrm
NEWS2x.txt271720644editdlrm
News3.txt568180644editdlrm
outwin.py57050644editdlrm
parenmatch.py72040644editdlrm
pathbrowser.py30930644editdlrm
percolator.py35680644editdlrm
pyparse.py198640644editdlrm
pyshell.py624840755editdlrm
query.py150670644editdlrm
README.txt116530644editdlrm
redirector.py68320644editdlrm
replace.py97980644editdlrm
rpc.py210780644editdlrm
run.py218770644editdlrm
runscript.py82730644editdlrm
scrolledlist.py44780644editdlrm
search.py55670644editdlrm
searchbase.py78520644editdlrm
searchengine.py73720644editdlrm
sidebar.py203270644editdlrm
squeezer.py128340644editdlrm
stackviewer.py40160644editdlrm
statusbar.py14740644editdlrm
textview.py68080644editdlrm
TODO.txt84770644editdlrm
tooltip.py66650644editdlrm
tree.py167730644editdlrm
undo.py110160644editdlrm
util.py13120644editdlrm
window.py26160644editdlrm
zoomheight.py42030644editdlrm
zzdummy.py20050644editdlrm
__init__.py3960644editdlrm
__main__.py1070644editdlrm
Edit: /opt/imh/python3.13/lib/python3.13/idlelib/tree.py (16773B)
# XXX TO DO: # - popup menu # - support partial or total redisplay # - key bindings (instead of quick-n-dirty bindings on Canvas): # - up/down arrow keys to move focus around # - ditto for page up/down, home/end # - left/right arrows to expand/collapse & move out/in # - more doc strings # - add icons for "file", "module", "class", "method"; better "python" icon # - callback for selection??? # - multiple-item selection # - tooltips # - redo geometry without magic numbers # - keep track of object ids to allow more careful cleaning # - optimize tree redraw after expand of subnode import os from tkinter import * from tkinter.ttk import Frame, Scrollbar from idlelib.config import idleConf from idlelib import zoomheight ICONDIR = "Icons" # Look for Icons subdirectory in the same directory as this module try: _icondir = os.path.join(os.path.dirname(__file__), ICONDIR) except NameError: _icondir = ICONDIR if os.path.isdir(_icondir): ICONDIR = _icondir elif not os.path.isdir(ICONDIR): raise RuntimeError(f"can't find icon directory ({ICONDIR!r})") def listicons(icondir=ICONDIR): """Utility to display the available icons.""" root = Tk() import glob list = glob.glob(os.path.join(glob.escape(icondir), "*.gif")) list.sort() images = [] row = column = 0 for file in list: name = os.path.splitext(os.path.basename(file))[0] image = PhotoImage(file=file, master=root) images.append(image) label = Label(root, image=image, bd=1, relief="raised") label.grid(row=row, column=column) label = Label(root, text=name) label.grid(row=row+1, column=column) column = column + 1 if column >= 10: row = row+2 column = 0 root.images = images def wheel_event(event, widget=None): """Handle scrollwheel event. For wheel up, event.delta = 120*n on Windows, -1*n on darwin, where n can be > 1 if one scrolls fast. Flicking the wheel generates up to maybe 20 events with n up to 10 or more 1. Macs use wheel down (delta = 1*n) to scroll up, so positive delta means to scroll up on both systems. X-11 sends Control-Button-4,5 events instead. The widget parameter is needed so browser label bindings can pass the underlying canvas. This function depends on widget.yview to not be overridden by a subclass. """ up = {EventType.MouseWheel: event.delta > 0, EventType.ButtonPress: event.num == 4} lines = -5 if up[event.type] else 5 widget = event.widget if widget is None else widget widget.yview(SCROLL, lines, 'units') return 'break' class TreeNode: dy = 0 def __init__(self, canvas, parent, item): self.canvas = canvas self.parent = parent self.item = item self.state = 'collapsed' self.selected = False self.children = [] self.x = self.y = None self.iconimages = {} # cache of PhotoImage instances for icons def destroy(self): for c in self.children[:]: self.children.remove(c) c.destroy() self.parent = None def geticonimage(self, name): try: return self.iconimages[name] except KeyError: pass file, ext = os.path.splitext(name) ext = ext or ".gif" fullname = os.path.join(ICONDIR, file + ext) image = PhotoImage(master=self.canvas, file=fullname) self.iconimages[name] = image return image def select(self, event=None): if self.selected: return self.deselectall() self.selected = True self.canvas.delete(self.image_id) self.drawicon() self.drawtext() def deselect(self, event=None): if not self.selected: return self.selected = False self.canvas.delete(self.image_id) self.drawicon() self.drawtext() def deselectall(self): if self.parent: self.parent.deselectall() else: self.deselecttree() def deselecttree(self): if self.selected: self.deselect() for child in self.children: child.deselecttree() def flip(self, event=None): if self.state == 'expanded': self.collapse() else: self.expand() self.item.OnDoubleClick() return "break" def expand(self, event=None): if not self.item._IsExpandable(): return if self.state != 'expanded': self.state = 'expanded' self.update() self.view() def collapse(self, event=None): if self.state != 'collapsed': self.state = 'collapsed' self.update() def view(self): top = self.y - 2 bottom = self.lastvisiblechild().y + 17 height = bottom - top visible_top = self.canvas.canvasy(0) visible_height = self.canvas.winfo_height() visible_bottom = self.canvas.canvasy(visible_height) if visible_top <= top and bottom <= visible_bottom: return x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion']) if top >= visible_top and height <= visible_height: fraction = top + height - visible_height else: fraction = top fraction = float(fraction) / y1 self.canvas.yview_moveto(fraction) def lastvisiblechild(self): if self.children and self.state == 'expanded': return self.children[-1].lastvisiblechild() else: return self def update(self): if self.parent: self.parent.update() else: oldcursor = self.canvas['cursor'] self.canvas['cursor'] = "watch" self.canvas.update() self.canvas.delete(ALL) # XXX could be more subtle self.draw(7, 2) x0, y0, x1, y1 = self.canvas.bbox(ALL) self.canvas.configure(scrollregion=(0, 0, x1, y1)) self.canvas['cursor'] = oldcursor def draw(self, x, y): # XXX This hard-codes too many geometry constants! self.x, self.y = x, y self.drawicon() self.drawtext() if self.state != 'expanded': return y + TreeNode.dy # draw children if not self.children: sublist = self.item._GetSubList() if not sublist: # _IsExpandable() was mistaken; that's allowed return y + TreeNode.dy for item in sublist: child = self.__class__(self.canvas, self, item) self.children.append(child) cx = x+20 cy = y + TreeNode.dy cylast = 0 for child in self.children: cylast = cy self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50") cy = child.draw(cx, cy) if child.item._IsExpandable(): if child.state == 'expanded': iconname = "minusnode" callback = child.collapse else: iconname = "plusnode" callback = child.expand image = self.geticonimage(iconname) id = self.canvas.create_image(x+9, cylast+7, image=image) # XXX This leaks bindings until canvas is deleted: self.canvas.tag_bind(id, "<1>", callback) self.canvas.tag_bind(id, "", lambda x: None) id = self.canvas.create_line(x+9, y+10, x+9, cylast+7, ##stipple="gray50", # XXX Seems broken in Tk 8.0.x fill="gray50") self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2 return cy def drawicon(self): if self.selected: imagename = (self.item.GetSelectedIconName() or self.item.GetIconName() or "openfolder") else: imagename = self.item.GetIconName() or "folder" image = self.geticonimage(imagename) id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image) self.image_id = id self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "", self.flip) def drawtext(self): textx = self.x+20-1 texty = self.y-4 labeltext = self.item.GetLabelText() if labeltext: id = self.canvas.create_text(textx, texty, anchor="nw", text=labeltext) self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "", self.flip) x0, y0, x1, y1 = self.canvas.bbox(id) textx = max(x1, 200) + 10 text = self.item.GetText() or "" try: self.entry except AttributeError: pass else: self.edit_finish() try: self.label except AttributeError: # padding carefully selected (on Windows) to match Entry widget: self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) theme = idleConf.CurrentTheme() if self.selected: self.label.configure(idleConf.GetHighlight(theme, 'hilite')) else: self.label.configure(idleConf.GetHighlight(theme, 'normal')) id = self.canvas.create_window(textx, texty, anchor="nw", window=self.label) self.label.bind("<1>", self.select_or_edit) self.label.bind("", self.flip) self.label.bind("", lambda e: wheel_event(e, self.canvas)) if self.label._windowingsystem == 'x11': self.label.bind("", lambda e: wheel_event(e, self.canvas)) self.label.bind("", lambda e: wheel_event(e, self.canvas)) self.text_id = id if TreeNode.dy == 0: # The first row doesn't matter what the dy is, just measure its # size to get the value of the subsequent dy coords = self.canvas.bbox(id) TreeNode.dy = max(20, coords[3] - coords[1] - 3) def select_or_edit(self, event=None): if self.selected and self.item.IsEditable(): self.edit(event) else: self.select(event) def edit(self, event=None): self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0) self.entry.insert(0, self.label['text']) self.entry.selection_range(0, END) self.entry.pack(ipadx=5) self.entry.focus_set() self.entry.bind("", self.edit_finish) self.entry.bind("", self.edit_cancel) def edit_finish(self, event=None): try: entry = self.entry del self.entry except AttributeError: return text = entry.get() entry.destroy() if text and text != self.item.GetText(): self.item.SetText(text) text = self.item.GetText() self.label['text'] = text self.drawtext() self.canvas.focus_set() def edit_cancel(self, event=None): try: entry = self.entry del self.entry except AttributeError: return entry.destroy() self.drawtext() self.canvas.focus_set() class TreeItem: """Abstract class representing tree items. Methods should typically be overridden, otherwise a default action is used. """ def __init__(self): """Constructor. Do whatever you need to do.""" def GetText(self): """Return text string to display.""" def GetLabelText(self): """Return label text string to display in front of text (if any).""" expandable = None def _IsExpandable(self): """Do not override! Called by TreeNode.""" if self.expandable is None: self.expandable = self.IsExpandable() return self.expandable def IsExpandable(self): """Return whether there are subitems.""" return 1 def _GetSubList(self): """Do not override! Called by TreeNode.""" if not self.IsExpandable(): return [] sublist = self.GetSubList() if not sublist: self.expandable = 0 return sublist def IsEditable(self): """Return whether the item's text may be edited.""" def SetText(self, text): """Change the item's text (if it is editable).""" def GetIconName(self): """Return name of icon to be displayed normally.""" def GetSelectedIconName(self): """Return name of icon to be displayed when selected.""" def GetSubList(self): """Return list of items forming sublist.""" def OnDoubleClick(self): """Called on a double-click on the item.""" # Example application class FileTreeItem(TreeItem): """Example TreeItem subclass -- browse the file system.""" def __init__(self, path): self.path = path def GetText(self): return os.path.basename(self.path) or self.path def IsEditable(self): return os.path.basename(self.path) != "" def SetText(self, text): newpath = os.path.dirname(self.path) newpath = os.path.join(newpath, text) if os.path.dirname(newpath) != os.path.dirname(self.path): return try: os.rename(self.path, newpath) self.path = newpath except OSError: pass def GetIconName(self): if not self.IsExpandable(): return "python" # XXX wish there was a "file" icon def IsExpandable(self): return os.path.isdir(self.path) def GetSubList(self): try: names = os.listdir(self.path) except OSError: return [] names.sort(key = os.path.normcase) sublist = [] for name in names: item = FileTreeItem(os.path.join(self.path, name)) sublist.append(item) return sublist # A canvas widget with scroll bars and some useful bindings class ScrolledCanvas: def __init__(self, master, **opts): if 'yscrollincrement' not in opts: opts['yscrollincrement'] = 17 self.master = master self.frame = Frame(master) self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) self.canvas = Canvas(self.frame, **opts) self.canvas.grid(row=0, column=0, sticky="nsew") self.vbar = Scrollbar(self.frame, name="vbar") self.vbar.grid(row=0, column=1, sticky="nse") self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal") self.hbar.grid(row=1, column=0, sticky="ews") self.canvas['yscrollcommand'] = self.vbar.set self.vbar['command'] = self.canvas.yview self.canvas['xscrollcommand'] = self.hbar.set self.hbar['command'] = self.canvas.xview self.canvas.bind("", self.page_up) self.canvas.bind("", self.page_down) self.canvas.bind("", self.unit_up) self.canvas.bind("", self.unit_down) self.canvas.bind("", wheel_event) if self.canvas._windowingsystem == 'x11': self.canvas.bind("", wheel_event) self.canvas.bind("", wheel_event) #if isinstance(master, Toplevel) or isinstance(master, Tk): self.canvas.bind("", self.zoom_height) self.canvas.focus_set() def page_up(self, event): self.canvas.yview_scroll(-1, "page") return "break" def page_down(self, event): self.canvas.yview_scroll(1, "page") return "break" def unit_up(self, event): self.canvas.yview_scroll(-1, "unit") return "break" def unit_down(self, event): self.canvas.yview_scroll(1, "unit") return "break" def zoom_height(self, event): zoomheight.zoom_height(self.master) return "break" def _tree_widget(parent): # htest # top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" % (x+50, y+175)) sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both", side=LEFT) item = FileTreeItem(ICONDIR) node = TreeNode(sc.canvas, None, item) node.expand() if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_tree', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_tree_widget)