Rewritten based on TreeWidget.py
diff --git a/Tools/idle/ClassBrowser.py b/Tools/idle/ClassBrowser.py
index f9b1655..0cf978e 100644
--- a/Tools/idle/ClassBrowser.py
+++ b/Tools/idle/ClassBrowser.py
@@ -1,168 +1,220 @@
-"""Primitive class browser.
+"""Class browser.
XXX TO DO:
-- reparse when source changed
+- reparse when source changed (maybe just a button would be OK?)
+ (or recheck on window popup)
- add popup menu with more options (e.g. doc strings, base classes, imports)
-- show function argument list (have to do pattern matching on source)
+- show function argument list? (have to do pattern matching on source)
- should the classes and methods lists also be in the module's menu bar?
+- add base classes to class browser tree
+- make methodless classes inexpandable
+- make classless modules inexpandable
+
"""
import os
+import sys
import string
import pyclbr
-from Tkinter import *
-import tkMessageBox
+
+import PyShell
from WindowList import ListedToplevel
-from Separator import HSeparator
-
-from ScrolledList import ScrolledList
-
+from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
class ClassBrowser:
- def __init__(self, flist, name, path=[]):
- root = flist.root
- try:
- dict = pyclbr.readmodule(name, path)
- except ImportError, msg:
- tkMessageBox.showerror("Import error", str(msg), parent=root)
- return
- if not dict:
- tkMessageBox.showerror("Nothing to browse",
- "Module %s defines no classes" % name, parent=root)
- return
- self.flist = flist
- self.dict = dict
- self.root = root
- self.top = top = ListedToplevel(root)
- self.top.protocol("WM_DELETE_WINDOW", self.close)
- self.top.bind("<Escape>", self.close)
- top.wm_title("Class Browser - " + name)
- top.wm_iconname("ClBrowser")
- self.sepa = HSeparator(top)
- leftframe, rightframe = self.sepa.parts()
- self.leftframe = leftframe
- self.rightframe = rightframe
- leftframe.pack(side="left", fill="both", expand=1)
- # Create help label
- self.helplabel = Label(leftframe, text="Module %s" % name,
- relief="groove", borderwidth=2)
- self.helplabel.pack(fill="x")
- # Create top frame, with scrollbar and listbox
- self.classviewer = ClassViewer(
- self.leftframe, self.flist, self)
- # Load the classes
- self.load_classes(dict, name)
+ def __init__(self, flist, name, path):
+ self.name = name
+ self.file = os.path.join(path[0], self.name + ".py")
+ self.init(flist)
def close(self, event=None):
- self.classviewer = None
- self.methodviewer = None
self.top.destroy()
- def load_classes(self, dict, module):
- self.classviewer.load_classes(dict, module)
- if self.methodframe:
- self.methodframe.destroy()
- self.methodframe = None
- self.methodviewer = None
-
- methodframe = None
- methodhelplabel = None
- methodviewer = None
-
- def show_methods(self, cl):
- if not self.methodframe:
- self.methodframe = Frame(self.rightframe)
- self.methodframe.pack(side="right", expand=1, fill="both")
- self.methodhelplabel = Label(self.methodframe,
- relief="groove", borderwidth=2)
- self.methodhelplabel.pack(fill="x")
- self.methodviewer = MethodViewer(self.methodframe, self.flist)
- self.methodhelplabel.config(text="Class %s" % cl.name)
- self.methodviewer.load_methods(cl)
-
-
-class ClassViewer(ScrolledList):
-
- def __init__(self, master, flist, browser):
- ScrolledList.__init__(self, master, width=40)
+ def init(self, flist):
self.flist = flist
- self.browser = browser
+ # reset pyclbr
+ pyclbr._modules.clear()
+ # create top
+ self.top = top = ListedToplevel(flist.root)
+ top.protocol("WM_DELETE_WINDOW", self.close)
+ top.bind("<Escape>", self.close)
+ self.settitle()
+ top.focus_set()
+ # create scrolled canvas
+ sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both")
+ item = self.rootnode()
+ node = TreeNode(sc.canvas, None, item)
+ node.update()
+ node.expand()
- def load_classes(self, dict, module):
- self.clear()
- self.dict = dict
+ def settitle(self):
+ self.top.wm_title("Class Browser - " + self.name)
+ self.top.wm_iconname("Class Browser")
+
+ def rootnode(self):
+ return ModuleBrowserTreeItem(self.file)
+
+class ModuleBrowserTreeItem(TreeItem):
+
+ def __init__(self, file):
+ self.file = file
+
+ def GetText(self):
+ return os.path.basename(self.file)
+
+ def GetIconName(self):
+ return "python"
+
+ def GetSubList(self):
+ sublist = []
+ for name in self.listclasses():
+ item = ClassBrowserTreeItem(name, self.classes, self.file)
+ sublist.append(item)
+ return sublist
+
+ def OnDoubleClick(self):
+ if os.path.normcase(self.file[-3:]) != ".py":
+ return
+ if not os.path.exists(self.file):
+ return
+ PyShell.flist.open(self.file)
+
+ def IsExpandable(self):
+ return os.path.normcase(self.file[-3:]) == ".py"
+
+ def listclasses(self):
+ dir, file = os.path.split(self.file)
+ name, ext = os.path.splitext(file)
+ if os.path.normcase(ext) != ".py":
+ return []
+ try:
+ dict = pyclbr.readmodule(name, [dir] + sys.path)
+ except ImportError, msg:
+ return []
items = []
- for key, value in dict.items():
- if value.module == module:
- items.append((value.lineno, key, value))
+ self.classes = {}
+ for key, cl in dict.items():
+ if cl.module == name:
+ s = key
+ if cl.super:
+ supers = []
+ for sup in cl.super:
+ if type(sup) is type(''):
+ sname = sup
+ else:
+ sname = sup.name
+ if sup.module != cl.module:
+ sname = "%s.%s" % (sup.module, sname)
+ supers.append(sname)
+ s = s + "(%s)" % string.join(supers, ", ")
+ items.append((cl.lineno, s))
+ self.classes[s] = cl
items.sort()
- for lineno, key, value in items:
- s = key
- if value.super:
- super = []
- for sup in value.super:
- name = sup.name
- if sup.module != value.module:
- name = "%s.%s" % (sup.module, name)
- super.append(name)
- s = s + "(%s)" % string.join(super, ", ")
- self.append(s)
+ list = []
+ for item, s in items:
+ list.append(s)
+ return list
- def getname(self, index):
- name = self.listbox.get(index)
- i = string.find(name, '(')
- if i >= 0:
+class ClassBrowserTreeItem(TreeItem):
+
+ def __init__(self, name, classes, file):
+ self.name = name
+ self.classes = classes
+ self.file = file
+
+ def GetText(self):
+ return "class " + self.name
+
+ def IsExpandable(self):
+ try:
+ cl = self.classes[self.name]
+ except (IndexError, KeyError):
+ return 0
+ else:
+ return not not cl.methods
+
+ def GetSubList(self):
+ sublist = []
+ for name in self.listmethods():
+ item = MethodBrowserTreeItem(
+ name, self.classes[self.name], self.file)
+ sublist.append(item)
+ return sublist
+
+ def OnDoubleClick(self):
+ if not os.path.exists(self.file):
+ return
+ edit = PyShell.flist.open(self.file)
+ if self.classes.has_key(self.name):
+ cl = self.classes[self.name]
+ else:
+ name = self.name
+ i = string.find(name, '(')
+ if i < 0:
+ return
name = name[:i]
- return name
+ if not self.classes.has_key(name):
+ return
+ cl = self.classes[name]
+ if not hasattr(cl, 'lineno'):
+ return
+ lineno = cl.lineno
+ edit.gotoline(lineno)
- def getclass(self, index):
- return self.dict[self.getname(index)]
-
- def on_select(self, index):
- self.show_methods(index)
-
- def on_double(self, index):
- self.show_source(index)
-
- def show_methods(self, index):
- cl = self.getclass(index)
- self.browser.show_methods(cl)
-
- def show_source(self, index):
- cl = self.getclass(index)
- if os.path.isfile(cl.file):
- edit = self.flist.open(cl.file)
- edit.gotoline(cl.lineno)
-
-
-class MethodViewer(ScrolledList):
-
- def __init__(self, master, flist):
- ScrolledList.__init__(self, master)
- self.flist = flist
-
- classinfo = None
-
- def load_methods(self, cl):
- self.classinfo = cl
- self.clear()
+ def listmethods(self):
+ try:
+ cl = self.classes[self.name]
+ except (IndexError, KeyError):
+ return []
items = []
for name, lineno in cl.methods.items():
items.append((lineno, name))
items.sort()
+ list = []
for item, name in items:
- self.append(name)
+ list.append(name)
+ return list
- def click_event(self, event):
- pass
+class MethodBrowserTreeItem(TreeItem):
- def on_double(self, index):
- self.show_source(self.get(index))
+ def __init__(self, name, cl, file):
+ self.name = name
+ self.cl = cl
+ self.file = file
- def show_source(self, name):
- if os.path.isfile(self.classinfo.file):
- edit = self.flist.open(self.classinfo.file)
- edit.gotoline(self.classinfo.methods[name])
+ def GetText(self):
+ return "def " + self.name + "(...)"
+
+ def GetIconName(self):
+ return "python" # XXX
+
+ def IsExpandable(self):
+ return 0
+
+ def OnDoubleClick(self):
+ if not os.path.exists(self.file):
+ return
+ edit = PyShell.flist.open(self.file)
+ edit.gotoline(self.cl.methods[self.name])
+
+def main():
+ try:
+ file = __file__
+ except NameError:
+ file = sys.argv[0]
+ if sys.argv[1:]:
+ file = sys.argv[1]
+ else:
+ file = sys.argv[0]
+ dir, file = os.path.split(file)
+ name = os.path.splitext(file)[0]
+ ClassBrowser(PyShell.flist, name, [dir])
+ if sys.stdin is sys.__stdin__:
+ mainloop()
+
+if __name__ == "__main__":
+ main()
diff --git a/Tools/idle/PathBrowser.py b/Tools/idle/PathBrowser.py
index 62470b2..7ea2410 100644
--- a/Tools/idle/PathBrowser.py
+++ b/Tools/idle/PathBrowser.py
@@ -1,58 +1,76 @@
import os
import sys
import imp
-import string
-import tkMessageBox
-from MultiScrolledLists import MultiScrolledLists
+from TreeWidget import TreeItem
+from ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
-class PathBrowser(MultiScrolledLists):
+class PathBrowser(ClassBrowser):
def __init__(self, flist):
- self.flist = flist
- MultiScrolledLists.__init__(self, flist.root, 4)
-
- def longtitle(self):
- return "Path Browser"
-
- def width(self, i):
- return 30
-
- def height(self, i):
- return 20
-
- def subtitle(self, i):
- if i == 0:
- return "Path Entries (sys.path)"
- if i-1 >= len(self.path):
- return ""
- if i == 1:
- return self.path[0]
- if i == 2:
- return "Classes in " + self.path[1]
- if i == 3:
- s = self.path[2]
- i = string.find(s, "(")
- if i > 0:
- s = s[:i]
- return "Methods of " + s
- return ""
-
- def items(self, i):
- if i == 0:
- return sys.path
- if i == 1:
- return self.listmodules()
- if i == 2:
- return self.listclasses()
- if i == 3:
- return self.listmethods()
-
- def listmodules(self):
- dir = self.path[0] or os.curdir
+ self.init(flist)
+
+ def settitle(self):
+ self.top.wm_title("Path Browser")
+ self.top.wm_iconname("Path Browser")
+
+ def rootnode(self):
+ return PathBrowserTreeItem()
+
+class PathBrowserTreeItem(TreeItem):
+
+ def GetText(self):
+ return "sys.path"
+
+ def GetSubList(self):
+ sublist = []
+ for dir in sys.path:
+ item = DirBrowserTreeItem(dir)
+ sublist.append(item)
+ return sublist
+
+class DirBrowserTreeItem(TreeItem):
+
+ def __init__(self, dir, packages=[]):
+ self.dir = dir
+ self.packages = packages
+
+ def GetText(self):
+ if not self.packages:
+ return self.dir
+ else:
+ return self.packages[-1] + ": package"
+
+ def GetSubList(self):
+ try:
+ names = os.listdir(self.dir or os.curdir)
+ except os.error:
+ return []
+ packages = []
+ for name in names:
+ file = os.path.join(self.dir, name)
+ if self.ispackagedir(file):
+ nn = os.path.normcase(name)
+ packages.append((nn, name, file))
+ packages.sort()
+ sublist = []
+ for nn, name, file in packages:
+ item = DirBrowserTreeItem(file, self.packages + [name])
+ sublist.append(item)
+ for nn, name in self.listmodules(names):
+ item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
+ sublist.append(item)
+ return sublist
+
+ def ispackagedir(self, file):
+ if not os.path.isdir(file):
+ return 0
+ init = os.path.join(file, "__init__.py")
+ return os.path.exists(init)
+
+ def listmodules(self, allnames):
modules = {}
suffixes = imp.get_suffixes()
- allnames = os.listdir(dir)
sorted = []
for suff, mode, flag in suffixes:
i = -len(suff)
@@ -65,96 +83,13 @@
sorted.append((normed_name, name))
allnames.remove(name)
sorted.sort()
- names = []
- for nn, name in sorted:
- names.append(name)
- return names
-
- def listclasses(self):
- import pyclbr
- dir = self.path[0]
- file = self.path[1]
- name, ext = os.path.splitext(file)
- if os.path.normcase(ext) != ".py":
- self.top.bell()
- return []
- try:
- self.top.configure(cursor="watch")
- self.top.update_idletasks()
- try:
- dict = pyclbr.readmodule(name, [dir] + sys.path)
- finally:
- self.top.configure(cursor="")
- except ImportError, msg:
- tkMessageBox.showerror("Import error", str(msg), parent=root)
- return []
- items = []
- self.classes = {}
- for key, cl in dict.items():
- if cl.module == name:
- s = key
- if cl.super:
- supers = []
- for sup in cl.super:
- if type(sup) is type(''):
- sname = sup
- else:
- sname = sup.name
- if sup.module != cl.module:
- sname = "%s.%s" % (sup.module, sname)
- supers.append(sname)
- s = s + "(%s)" % string.join(supers, ", ")
- items.append((cl.lineno, s))
- self.classes[s] = cl
- items.sort()
- list = []
- for item, s in items:
- list.append(s)
- return list
-
- def listmethods(self):
- try:
- cl = self.classes[self.path[2]]
- except (IndexError, KeyError):
- return []
- items = []
- for name, lineno in cl.methods.items():
- items.append((lineno, name))
- items.sort()
- list = []
- for item, name in items:
- list.append(name)
- return list
-
- def on_double(self, index, i):
- if i == 0:
- return
- if i >= 1:
- dir = self.path[0]
- file = self.path[1]
- name, ext = os.path.splitext(file)
- if os.path.normcase(ext) != ".py":
- self.top.bell()
- return
- fullname = os.path.join(dir, file)
- edit = self.flist.open(fullname)
- if i >= 2:
- classname = self.path[2]
- try:
- cl = self.classes[classname]
- except KeyError:
- cl = None
- else:
- if i == 2:
- edit.gotoline(cl.lineno)
- else:
- methodname = self.path[3]
- edit.gotoline(cl.methods[methodname])
-
+ return sorted
def main():
import PyShell
PathBrowser(PyShell.flist)
+ if sys.stdin is sys.__stdin__:
+ mainloop()
if __name__ == "__main__":
main()