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()
def load_classes(self, dict, module): # create top
self.clear() self.top = top = ListedToplevel(flist.root)
self.dict = dict 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 = [] 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:
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() items.sort()
for lineno, key, value in items: list = []
s = key for item, s in items:
if value.super: list.append(s)
super = [] return list
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
def getclass(self, index): class ClassBrowserTreeItem(TreeItem):
return self.dict[self.getname(index)]
def on_select(self, index): def __init__(self, name, classes, file):
self.show_methods(index) self.name = name
self.classes = classes
self.file = file
def on_double(self, index): def GetText(self):
self.show_source(index) return "class " + self.name
def show_methods(self, index): def IsExpandable(self):
cl = self.getclass(index) try:
self.browser.show_methods(cl) cl = self.classes[self.name]
except (IndexError, KeyError):
def show_source(self, index): return 0
cl = self.getclass(index) else:
if os.path.isfile(cl.file): return not not cl.methods
edit = self.flist.open(cl.file)
edit.gotoline(cl.lineno) def GetSubList(self):
sublist = []
for name in self.listmethods():
class MethodViewer(ScrolledList): item = MethodBrowserTreeItem(
name, self.classes[self.name], self.file)
def __init__(self, master, flist): sublist.append(item)
ScrolledList.__init__(self, master) return sublist
self.flist = flist
def OnDoubleClick(self):
classinfo = None 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 click_event(self, event): def __init__(self, name, cl, file):
pass self.name = name
self.cl = cl
self.file = file
def on_double(self, index): def GetText(self):
self.show_source(self.get(index)) return "def " + self.name + "(...)"
def show_source(self, name): def GetIconName(self):
if os.path.isfile(self.classinfo.file): return "python" # XXX
edit = self.flist.open(self.classinfo.file)
edit.gotoline(self.classinfo.methods[name]) 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()
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