mirror of
https://github.com/python/cpython.git
synced 2025-07-24 03:35:53 +00:00
Rewritten based on TreeWidget.py
This commit is contained in:
parent
1ff48ec852
commit
ec9cca776a
2 changed files with 253 additions and 266 deletions
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue