blob: 71176cd701549d7ca82f42d321227a819f53fab8 [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
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000017from idlelib import PyShell
18from idlelib.WindowList import ListedToplevel
19from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
20from idlelib.configHandler import idleConf
David Scherer7aced172000-08-15 01:13:23 +000021
22class ClassBrowser:
23
24 def __init__(self, flist, name, path):
25 # XXX This API should change, if the file doesn't end in ".py"
26 # XXX the code here is bogus!
27 self.name = name
28 self.file = os.path.join(path[0], self.name + ".py")
29 self.init(flist)
30
31 def close(self, event=None):
32 self.top.destroy()
33 self.node.destroy()
34
35 def init(self, flist):
36 self.flist = flist
37 # reset pyclbr
38 pyclbr._modules.clear()
39 # create top
40 self.top = top = ListedToplevel(flist.root)
41 top.protocol("WM_DELETE_WINDOW", self.close)
42 top.bind("<Escape>", self.close)
43 self.settitle()
44 top.focus_set()
45 # create scrolled canvas
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000046 theme = idleConf.GetOption('main','Theme','name')
47 background = idleConf.GetHighlight(theme, 'normal')['background']
48 sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
David Scherer7aced172000-08-15 01:13:23 +000049 sc.frame.pack(expand=1, fill="both")
50 item = self.rootnode()
51 self.node = node = TreeNode(sc.canvas, None, item)
52 node.update()
53 node.expand()
54
55 def settitle(self):
56 self.top.wm_title("Class Browser - " + self.name)
57 self.top.wm_iconname("Class Browser")
58
59 def rootnode(self):
60 return ModuleBrowserTreeItem(self.file)
61
62class ModuleBrowserTreeItem(TreeItem):
63
64 def __init__(self, file):
65 self.file = file
66
67 def GetText(self):
68 return os.path.basename(self.file)
69
70 def GetIconName(self):
71 return "python"
72
73 def GetSubList(self):
74 sublist = []
75 for name in self.listclasses():
76 item = ClassBrowserTreeItem(name, self.classes, self.file)
77 sublist.append(item)
78 return sublist
79
80 def OnDoubleClick(self):
81 if os.path.normcase(self.file[-3:]) != ".py":
82 return
83 if not os.path.exists(self.file):
84 return
85 PyShell.flist.open(self.file)
86
87 def IsExpandable(self):
88 return os.path.normcase(self.file[-3:]) == ".py"
Kurt B. Kaiserd6c4c9e2001-07-12 23:54:20 +000089
David Scherer7aced172000-08-15 01:13:23 +000090 def listclasses(self):
91 dir, file = os.path.split(self.file)
92 name, ext = os.path.splitext(file)
93 if os.path.normcase(ext) != ".py":
94 return []
95 try:
96 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
Guido van Rossumb940e112007-01-10 16:19:56 +000097 except ImportError as msg:
David Scherer7aced172000-08-15 01:13:23 +000098 return []
99 items = []
100 self.classes = {}
101 for key, cl in dict.items():
102 if cl.module == name:
103 s = key
Raymond Hettinger65500512003-01-19 02:37:41 +0000104 if hasattr(cl, 'super') and cl.super:
David Scherer7aced172000-08-15 01:13:23 +0000105 supers = []
106 for sup in cl.super:
107 if type(sup) is type(''):
108 sname = sup
109 else:
110 sname = sup.name
111 if sup.module != cl.module:
112 sname = "%s.%s" % (sup.module, sname)
113 supers.append(sname)
Kurt B. Kaisera2876442002-09-15 22:09:16 +0000114 s = s + "(%s)" % ", ".join(supers)
David Scherer7aced172000-08-15 01:13:23 +0000115 items.append((cl.lineno, s))
116 self.classes[s] = cl
117 items.sort()
118 list = []
119 for item, s in items:
120 list.append(s)
121 return list
122
123class ClassBrowserTreeItem(TreeItem):
124
125 def __init__(self, name, classes, file):
126 self.name = name
127 self.classes = classes
128 self.file = file
129 try:
130 self.cl = self.classes[self.name]
131 except (IndexError, KeyError):
132 self.cl = None
133 self.isfunction = isinstance(self.cl, pyclbr.Function)
134
135 def GetText(self):
136 if self.isfunction:
137 return "def " + self.name + "(...)"
138 else:
139 return "class " + self.name
140
141 def GetIconName(self):
142 if self.isfunction:
143 return "python"
144 else:
145 return "folder"
146
147 def IsExpandable(self):
148 if self.cl:
Kurt B. Kaiser0b743442003-01-20 04:49:37 +0000149 try:
150 return not not self.cl.methods
151 except AttributeError:
152 return False
David Scherer7aced172000-08-15 01:13:23 +0000153
154 def GetSubList(self):
155 if not self.cl:
156 return []
157 sublist = []
158 for name in self.listmethods():
159 item = MethodBrowserTreeItem(name, self.cl, self.file)
160 sublist.append(item)
161 return sublist
162
163 def OnDoubleClick(self):
164 if not os.path.exists(self.file):
165 return
166 edit = PyShell.flist.open(self.file)
167 if hasattr(self.cl, 'lineno'):
168 lineno = self.cl.lineno
169 edit.gotoline(lineno)
170
171 def listmethods(self):
172 if not self.cl:
173 return []
174 items = []
175 for name, lineno in self.cl.methods.items():
176 items.append((lineno, name))
177 items.sort()
178 list = []
179 for item, name in items:
180 list.append(name)
181 return list
182
183class MethodBrowserTreeItem(TreeItem):
184
185 def __init__(self, name, cl, file):
186 self.name = name
187 self.cl = cl
188 self.file = file
189
190 def GetText(self):
191 return "def " + self.name + "(...)"
192
193 def GetIconName(self):
194 return "python" # XXX
195
196 def IsExpandable(self):
197 return 0
198
199 def OnDoubleClick(self):
200 if not os.path.exists(self.file):
201 return
202 edit = PyShell.flist.open(self.file)
203 edit.gotoline(self.cl.methods[self.name])
204
205def main():
206 try:
207 file = __file__
208 except NameError:
209 file = sys.argv[0]
210 if sys.argv[1:]:
211 file = sys.argv[1]
212 else:
213 file = sys.argv[0]
214 dir, file = os.path.split(file)
215 name = os.path.splitext(file)[0]
216 ClassBrowser(PyShell.flist, name, [dir])
217 if sys.stdin is sys.__stdin__:
218 mainloop()
219
220if __name__ == "__main__":
221 main()