diff options
Diffstat (limited to 'Tools/idle')
-rw-r--r-- | Tools/idle/ClassBrowser.py | 318 | ||||
-rw-r--r-- | Tools/idle/PathBrowser.py | 199 |
2 files changed, 252 insertions, 265 deletions
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 -from WindowList import ListedToplevel -from Separator import HSeparator - -from ScrolledList import ScrolledList +import PyShell +from WindowList import ListedToplevel +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 - - def load_classes(self, dict, module): - self.clear() - self.dict = dict + # 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 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) - - def getname(self, index): - name = self.listbox.get(index) - i = string.find(name, '(') - if i >= 0: - name = name[:i] - return name + list = [] + for item, s in items: + list.append(s) + return list - def getclass(self, index): - return self.dict[self.getname(index)] +class ClassBrowserTreeItem(TreeItem): - def on_select(self, index): - self.show_methods(index) + def __init__(self, name, classes, file): + self.name = name + self.classes = classes + self.file = file - def on_double(self, index): - self.show_source(index) + def GetText(self): + return "class " + self.name - 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 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] + 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 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 + +class MethodBrowserTreeItem(TreeItem): - def click_event(self, event): - pass + def __init__(self, name, cl, file): + self.name = name + self.cl = cl + self.file = file - def on_double(self, index): - self.show_source(self.get(index)) + def GetText(self): + return "def " + self.name + "(...)" - 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 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 @@ class PathBrowser(MultiScrolledLists): 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() |