blob: 82f51915d20eef6bcfadd9878f4b3e0268bc93b3 [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001"""Class browser.
2
3XXX TO DO:
4
5- reparse when source changed (maybe just a button would be OK?)
6 (or recheck on window popup)
7- add popup menu with more options (e.g. doc strings, base classes, imports)
8- show function argument list? (have to do pattern matching on source)
9- should the classes and methods lists also be in the module's menu bar?
10- add base classes to class browser tree
11"""
12
13import os
14import sys
David Scherer7aced172000-08-15 01:13:23 +000015import pyclbr
16
David Scherer7aced172000-08-15 01:13:23 +000017import PyShell
18from WindowList import ListedToplevel
19from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
20
21class ClassBrowser:
22
23 def __init__(self, flist, name, path):
24 # XXX This API should change, if the file doesn't end in ".py"
25 # XXX the code here is bogus!
26 self.name = name
27 self.file = os.path.join(path[0], self.name + ".py")
28 self.init(flist)
29
30 def close(self, event=None):
31 self.top.destroy()
32 self.node.destroy()
33
34 def init(self, flist):
35 self.flist = flist
36 # reset pyclbr
37 pyclbr._modules.clear()
38 # create top
39 self.top = top = ListedToplevel(flist.root)
40 top.protocol("WM_DELETE_WINDOW", self.close)
41 top.bind("<Escape>", self.close)
42 self.settitle()
43 top.focus_set()
44 # create scrolled canvas
45 sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
46 sc.frame.pack(expand=1, fill="both")
47 item = self.rootnode()
48 self.node = node = TreeNode(sc.canvas, None, item)
49 node.update()
50 node.expand()
51
52 def settitle(self):
53 self.top.wm_title("Class Browser - " + self.name)
54 self.top.wm_iconname("Class Browser")
55
56 def rootnode(self):
57 return ModuleBrowserTreeItem(self.file)
58
59class ModuleBrowserTreeItem(TreeItem):
60
61 def __init__(self, file):
62 self.file = file
63
64 def GetText(self):
65 return os.path.basename(self.file)
66
67 def GetIconName(self):
68 return "python"
69
70 def GetSubList(self):
71 sublist = []
72 for name in self.listclasses():
73 item = ClassBrowserTreeItem(name, self.classes, self.file)
74 sublist.append(item)
75 return sublist
76
77 def OnDoubleClick(self):
78 if os.path.normcase(self.file[-3:]) != ".py":
79 return
80 if not os.path.exists(self.file):
81 return
82 PyShell.flist.open(self.file)
83
84 def IsExpandable(self):
85 return os.path.normcase(self.file[-3:]) == ".py"
Kurt B. Kaiserd6c4c9e2001-07-12 23:54:20 +000086
David Scherer7aced172000-08-15 01:13:23 +000087 def listclasses(self):
88 dir, file = os.path.split(self.file)
89 name, ext = os.path.splitext(file)
90 if os.path.normcase(ext) != ".py":
91 return []
92 try:
93 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
94 except ImportError, msg:
95 return []
96 items = []
97 self.classes = {}
98 for key, cl in dict.items():
99 if cl.module == name:
100 s = key
Raymond Hettinger65500512003-01-19 02:37:41 +0000101 if hasattr(cl, 'super') and cl.super:
David Scherer7aced172000-08-15 01:13:23 +0000102 supers = []
103 for sup in cl.super:
104 if type(sup) is type(''):
105 sname = sup
106 else:
107 sname = sup.name
108 if sup.module != cl.module:
109 sname = "%s.%s" % (sup.module, sname)
110 supers.append(sname)
Kurt B. Kaisera2876442002-09-15 22:09:16 +0000111 s = s + "(%s)" % ", ".join(supers)
David Scherer7aced172000-08-15 01:13:23 +0000112 items.append((cl.lineno, s))
113 self.classes[s] = cl
114 items.sort()
115 list = []
116 for item, s in items:
117 list.append(s)
118 return list
119
120class ClassBrowserTreeItem(TreeItem):
121
122 def __init__(self, name, classes, file):
123 self.name = name
124 self.classes = classes
125 self.file = file
126 try:
127 self.cl = self.classes[self.name]
128 except (IndexError, KeyError):
129 self.cl = None
130 self.isfunction = isinstance(self.cl, pyclbr.Function)
131
132 def GetText(self):
133 if self.isfunction:
134 return "def " + self.name + "(...)"
135 else:
136 return "class " + self.name
137
138 def GetIconName(self):
139 if self.isfunction:
140 return "python"
141 else:
142 return "folder"
143
144 def IsExpandable(self):
145 if self.cl:
Kurt B. Kaiser0b743442003-01-20 04:49:37 +0000146 try:
147 return not not self.cl.methods
148 except AttributeError:
149 return False
David Scherer7aced172000-08-15 01:13:23 +0000150
151 def GetSubList(self):
152 if not self.cl:
153 return []
154 sublist = []
155 for name in self.listmethods():
156 item = MethodBrowserTreeItem(name, self.cl, self.file)
157 sublist.append(item)
158 return sublist
159
160 def OnDoubleClick(self):
161 if not os.path.exists(self.file):
162 return
163 edit = PyShell.flist.open(self.file)
164 if hasattr(self.cl, 'lineno'):
165 lineno = self.cl.lineno
166 edit.gotoline(lineno)
167
168 def listmethods(self):
169 if not self.cl:
170 return []
171 items = []
172 for name, lineno in self.cl.methods.items():
173 items.append((lineno, name))
174 items.sort()
175 list = []
176 for item, name in items:
177 list.append(name)
178 return list
179
180class MethodBrowserTreeItem(TreeItem):
181
182 def __init__(self, name, cl, file):
183 self.name = name
184 self.cl = cl
185 self.file = file
186
187 def GetText(self):
188 return "def " + self.name + "(...)"
189
190 def GetIconName(self):
191 return "python" # XXX
192
193 def IsExpandable(self):
194 return 0
195
196 def OnDoubleClick(self):
197 if not os.path.exists(self.file):
198 return
199 edit = PyShell.flist.open(self.file)
200 edit.gotoline(self.cl.methods[self.name])
201
202def main():
203 try:
204 file = __file__
205 except NameError:
206 file = sys.argv[0]
207 if sys.argv[1:]:
208 file = sys.argv[1]
209 else:
210 file = sys.argv[0]
211 dir, file = os.path.split(file)
212 name = os.path.splitext(file)[0]
213 ClassBrowser(PyShell.flist, name, [dir])
214 if sys.stdin is sys.__stdin__:
215 mainloop()
216
217if __name__ == "__main__":
218 main()