Rewritten based on TreeWidget.py

This commit is contained in:
Guido van Rossum 1999-06-01 18:21:31 +00:00
parent 1ff48ec852
commit ec9cca776a
2 changed files with 253 additions and 266 deletions

View file

@ -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()