aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib-python/3/idlelib/ClassBrowser.py')
-rw-r--r--lib-python/3/idlelib/ClassBrowser.py221
1 files changed, 221 insertions, 0 deletions
diff --git a/lib-python/3/idlelib/ClassBrowser.py b/lib-python/3/idlelib/ClassBrowser.py
new file mode 100644
index 0000000000..71176cd701
--- /dev/null
+++ b/lib-python/3/idlelib/ClassBrowser.py
@@ -0,0 +1,221 @@
+"""Class browser.
+
+XXX TO DO:
+
+- 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)
+- should the classes and methods lists also be in the module's menu bar?
+- add base classes to class browser tree
+"""
+
+import os
+import sys
+import pyclbr
+
+from idlelib import PyShell
+from idlelib.WindowList import ListedToplevel
+from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+from idlelib.configHandler import idleConf
+
+class ClassBrowser:
+
+ def __init__(self, flist, name, path):
+ # XXX This API should change, if the file doesn't end in ".py"
+ # XXX the code here is bogus!
+ self.name = name
+ self.file = os.path.join(path[0], self.name + ".py")
+ self.init(flist)
+
+ def close(self, event=None):
+ self.top.destroy()
+ self.node.destroy()
+
+ def init(self, flist):
+ self.flist = flist
+ # 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
+ theme = idleConf.GetOption('main','Theme','name')
+ background = idleConf.GetHighlight(theme, 'normal')['background']
+ sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both")
+ item = self.rootnode()
+ self.node = 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_ex(name, [dir] + sys.path)
+ except ImportError as msg:
+ return []
+ items = []
+ self.classes = {}
+ for key, cl in dict.items():
+ if cl.module == name:
+ s = key
+ if hasattr(cl, 'super') and 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)" % ", ".join(supers)
+ items.append((cl.lineno, s))
+ self.classes[s] = cl
+ items.sort()
+ list = []
+ for item, s in items:
+ list.append(s)
+ return list
+
+class ClassBrowserTreeItem(TreeItem):
+
+ def __init__(self, name, classes, file):
+ self.name = name
+ self.classes = classes
+ self.file = file
+ try:
+ self.cl = self.classes[self.name]
+ except (IndexError, KeyError):
+ self.cl = None
+ self.isfunction = isinstance(self.cl, pyclbr.Function)
+
+ def GetText(self):
+ if self.isfunction:
+ return "def " + self.name + "(...)"
+ else:
+ return "class " + self.name
+
+ def GetIconName(self):
+ if self.isfunction:
+ return "python"
+ else:
+ return "folder"
+
+ def IsExpandable(self):
+ if self.cl:
+ try:
+ return not not self.cl.methods
+ except AttributeError:
+ return False
+
+ def GetSubList(self):
+ if not self.cl:
+ return []
+ sublist = []
+ for name in self.listmethods():
+ item = MethodBrowserTreeItem(name, self.cl, 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 hasattr(self.cl, 'lineno'):
+ lineno = self.cl.lineno
+ edit.gotoline(lineno)
+
+ def listmethods(self):
+ if not self.cl:
+ return []
+ items = []
+ for name, lineno in self.cl.methods.items():
+ items.append((lineno, name))
+ items.sort()
+ list = []
+ for item, name in items:
+ list.append(name)
+ return list
+
+class MethodBrowserTreeItem(TreeItem):
+
+ def __init__(self, name, cl, file):
+ self.name = name
+ self.cl = cl
+ self.file = file
+
+ 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()