Kaydet (Commit) ec9cca77 authored tarafından Guido van Rossum's avatar Guido van Rossum

Rewritten based on TreeWidget.py

üst 1ff48ec8
"""Primitive class browser. """Class browser.
XXX TO DO: 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) - 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? - 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 os
import sys
import string import string
import pyclbr 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: class ClassBrowser:
def __init__(self, flist, name, path=[]): def __init__(self, flist, name, path):
root = flist.root self.name = name
try: self.file = os.path.join(path[0], self.name + ".py")
dict = pyclbr.readmodule(name, path) self.init(flist)
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 close(self, event=None): def close(self, event=None):
self.classviewer = None
self.methodviewer = None
self.top.destroy() self.top.destroy()
def load_classes(self, dict, module): def init(self, flist):
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)
self.flist = 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 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 load_classes(self, dict, module): def listclasses(self):
self.clear() dir, file = os.path.split(self.file)
self.dict = dict 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 = [] items = []
for key, value in dict.items(): self.classes = {}
if value.module == module: for key, cl in dict.items():
items.append((value.lineno, key, value)) if cl.module == name:
items.sort()
for lineno, key, value in items:
s = key s = key
if value.super: if cl.super:
super = [] supers = []
for sup in value.super: for sup in cl.super:
name = sup.name if type(sup) is type(''):
if sup.module != value.module: sname = sup
name = "%s.%s" % (sup.module, name) else:
super.append(name) sname = sup.name
s = s + "(%s)" % string.join(super, ", ") if sup.module != cl.module:
self.append(s) sname = "%s.%s" % (sup.module, sname)
supers.append(sname)
def getname(self, index): s = s + "(%s)" % string.join(supers, ", ")
name = self.listbox.get(index) items.append((cl.lineno, s))
i = string.find(name, '(') self.classes[s] = cl
if i >= 0: items.sort()
name = name[:i] list = []
return name for item, s in items:
list.append(s)
def getclass(self, index): return list
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 ClassBrowserTreeItem(TreeItem):
class MethodViewer(ScrolledList): def __init__(self, name, classes, file):
self.name = name
self.classes = classes
self.file = file
def __init__(self, master, flist): def GetText(self):
ScrolledList.__init__(self, master) return "class " + self.name
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): def listmethods(self):
self.classinfo = cl try:
self.clear() cl = self.classes[self.name]
except (IndexError, KeyError):
return []
items = [] items = []
for name, lineno in cl.methods.items(): for name, lineno in cl.methods.items():
items.append((lineno, name)) items.append((lineno, name))
items.sort() items.sort()
list = []
for item, name in items: for item, name in items:
self.append(name) list.append(name)
return list
class MethodBrowserTreeItem(TreeItem):
def __init__(self, name, cl, file):
self.name = name
self.cl = cl
self.file = file
def click_event(self, event): def GetText(self):
pass return "def " + self.name + "(...)"
def on_double(self, index): def GetIconName(self):
self.show_source(self.get(index)) return "python" # XXX
def show_source(self, name): def IsExpandable(self):
if os.path.isfile(self.classinfo.file): return 0
edit = self.flist.open(self.classinfo.file)
edit.gotoline(self.classinfo.methods[name]) 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()
import os import os
import sys import sys
import imp 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): def __init__(self, flist):
self.flist = flist self.init(flist)
MultiScrolledLists.__init__(self, flist.root, 4)
def settitle(self):
def longtitle(self): self.top.wm_title("Path Browser")
return "Path Browser" self.top.wm_iconname("Path Browser")
def width(self, i): def rootnode(self):
return 30 return PathBrowserTreeItem()
def height(self, i): class PathBrowserTreeItem(TreeItem):
return 20
def GetText(self):
def subtitle(self, i): return "sys.path"
if i == 0:
return "Path Entries (sys.path)" def GetSubList(self):
if i-1 >= len(self.path): sublist = []
return "" for dir in sys.path:
if i == 1: item = DirBrowserTreeItem(dir)
return self.path[0] sublist.append(item)
if i == 2: return sublist
return "Classes in " + self.path[1]
if i == 3: class DirBrowserTreeItem(TreeItem):
s = self.path[2]
i = string.find(s, "(") def __init__(self, dir, packages=[]):
if i > 0: self.dir = dir
s = s[:i] self.packages = packages
return "Methods of " + s
return "" def GetText(self):
if not self.packages:
def items(self, i): return self.dir
if i == 0: else:
return sys.path return self.packages[-1] + ": package"
if i == 1:
return self.listmodules() def GetSubList(self):
if i == 2: try:
return self.listclasses() names = os.listdir(self.dir or os.curdir)
if i == 3: except os.error:
return self.listmethods() return []
packages = []
def listmodules(self): for name in names:
dir = self.path[0] or os.curdir 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 = {} modules = {}
suffixes = imp.get_suffixes() suffixes = imp.get_suffixes()
allnames = os.listdir(dir)
sorted = [] sorted = []
for suff, mode, flag in suffixes: for suff, mode, flag in suffixes:
i = -len(suff) i = -len(suff)
...@@ -65,96 +83,13 @@ class PathBrowser(MultiScrolledLists): ...@@ -65,96 +83,13 @@ class PathBrowser(MultiScrolledLists):
sorted.append((normed_name, name)) sorted.append((normed_name, name))
allnames.remove(name) allnames.remove(name)
sorted.sort() sorted.sort()
names = [] return sorted
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])
def main(): def main():
import PyShell import PyShell
PathBrowser(PyShell.flist) PathBrowser(PyShell.flist)
if sys.stdin is sys.__stdin__:
mainloop()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment