blob: 0cf978e18a2c9c4ef1ffd11c206d2818a85bd971 [file] [log] [blame]
Guido van Rossumec9cca71999-06-01 18:21:31 +00001"""Class browser.
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +00002
3XXX TO DO:
Guido van Rossum504b0bf1999-01-02 21:28:54 +00004
Guido van Rossumec9cca71999-06-01 18:21:31 +00005- reparse when source changed (maybe just a button would be OK?)
6 (or recheck on window popup)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +00007- add popup menu with more options (e.g. doc strings, base classes, imports)
Guido van Rossumec9cca71999-06-01 18:21:31 +00008- show function argument list? (have to do pattern matching on source)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +00009- should the classes and methods lists also be in the module's menu bar?
Guido van Rossumec9cca71999-06-01 18:21:31 +000010- add base classes to class browser tree
11- make methodless classes inexpandable
12- make classless modules inexpandable
13
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000014
15"""
16
Guido van Rossume6fae1c1998-10-15 23:27:08 +000017import os
Guido van Rossumec9cca71999-06-01 18:21:31 +000018import sys
Guido van Rossum439c4671998-10-13 03:59:57 +000019import string
20import pyclbr
Guido van Rossumec9cca71999-06-01 18:21:31 +000021
22import PyShell
Guido van Rossum504b0bf1999-01-02 21:28:54 +000023from WindowList import ListedToplevel
Guido van Rossumec9cca71999-06-01 18:21:31 +000024from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
Guido van Rossume6fae1c1998-10-15 23:27:08 +000025
Guido van Rossum439c4671998-10-13 03:59:57 +000026class ClassBrowser:
Guido van Rossum504b0bf1999-01-02 21:28:54 +000027
Guido van Rossumec9cca71999-06-01 18:21:31 +000028 def __init__(self, flist, name, path):
29 self.name = name
30 self.file = os.path.join(path[0], self.name + ".py")
31 self.init(flist)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000032
Guido van Rossumdc5066e1999-01-11 14:46:06 +000033 def close(self, event=None):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000034 self.top.destroy()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000035
Guido van Rossumec9cca71999-06-01 18:21:31 +000036 def init(self, flist):
Guido van Rossume6fae1c1998-10-15 23:27:08 +000037 self.flist = flist
Guido van Rossumec9cca71999-06-01 18:21:31 +000038 # reset pyclbr
39 pyclbr._modules.clear()
40 # create top
41 self.top = top = ListedToplevel(flist.root)
42 top.protocol("WM_DELETE_WINDOW", self.close)
43 top.bind("<Escape>", self.close)
44 self.settitle()
45 top.focus_set()
46 # create scrolled canvas
47 sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
48 sc.frame.pack(expand=1, fill="both")
49 item = self.rootnode()
50 node = TreeNode(sc.canvas, None, item)
51 node.update()
52 node.expand()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000053
Guido van Rossumec9cca71999-06-01 18:21:31 +000054 def settitle(self):
55 self.top.wm_title("Class Browser - " + self.name)
56 self.top.wm_iconname("Class Browser")
57
58 def rootnode(self):
59 return ModuleBrowserTreeItem(self.file)
60
61class ModuleBrowserTreeItem(TreeItem):
62
63 def __init__(self, file):
64 self.file = file
65
66 def GetText(self):
67 return os.path.basename(self.file)
68
69 def GetIconName(self):
70 return "python"
71
72 def GetSubList(self):
73 sublist = []
74 for name in self.listclasses():
75 item = ClassBrowserTreeItem(name, self.classes, self.file)
76 sublist.append(item)
77 return sublist
78
79 def OnDoubleClick(self):
80 if os.path.normcase(self.file[-3:]) != ".py":
81 return
82 if not os.path.exists(self.file):
83 return
84 PyShell.flist.open(self.file)
85
86 def IsExpandable(self):
87 return os.path.normcase(self.file[-3:]) == ".py"
88
89 def listclasses(self):
90 dir, file = os.path.split(self.file)
91 name, ext = os.path.splitext(file)
92 if os.path.normcase(ext) != ".py":
93 return []
94 try:
95 dict = pyclbr.readmodule(name, [dir] + sys.path)
96 except ImportError, msg:
97 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000098 items = []
Guido van Rossumec9cca71999-06-01 18:21:31 +000099 self.classes = {}
100 for key, cl in dict.items():
101 if cl.module == name:
102 s = key
103 if cl.super:
104 supers = []
105 for sup in cl.super:
106 if type(sup) is type(''):
107 sname = sup
108 else:
109 sname = sup.name
110 if sup.module != cl.module:
111 sname = "%s.%s" % (sup.module, sname)
112 supers.append(sname)
113 s = s + "(%s)" % string.join(supers, ", ")
114 items.append((cl.lineno, s))
115 self.classes[s] = cl
Guido van Rossum439c4671998-10-13 03:59:57 +0000116 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000117 list = []
118 for item, s in items:
119 list.append(s)
120 return list
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000121
Guido van Rossumec9cca71999-06-01 18:21:31 +0000122class ClassBrowserTreeItem(TreeItem):
123
124 def __init__(self, name, classes, file):
125 self.name = name
126 self.classes = classes
127 self.file = file
128
129 def GetText(self):
130 return "class " + self.name
131
132 def IsExpandable(self):
133 try:
134 cl = self.classes[self.name]
135 except (IndexError, KeyError):
136 return 0
137 else:
138 return not not cl.methods
139
140 def GetSubList(self):
141 sublist = []
142 for name in self.listmethods():
143 item = MethodBrowserTreeItem(
144 name, self.classes[self.name], self.file)
145 sublist.append(item)
146 return sublist
147
148 def OnDoubleClick(self):
149 if not os.path.exists(self.file):
150 return
151 edit = PyShell.flist.open(self.file)
152 if self.classes.has_key(self.name):
153 cl = self.classes[self.name]
154 else:
155 name = self.name
156 i = string.find(name, '(')
157 if i < 0:
158 return
Guido van Rossume6fae1c1998-10-15 23:27:08 +0000159 name = name[:i]
Guido van Rossumec9cca71999-06-01 18:21:31 +0000160 if not self.classes.has_key(name):
161 return
162 cl = self.classes[name]
163 if not hasattr(cl, 'lineno'):
164 return
165 lineno = cl.lineno
166 edit.gotoline(lineno)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000167
Guido van Rossumec9cca71999-06-01 18:21:31 +0000168 def listmethods(self):
169 try:
170 cl = self.classes[self.name]
171 except (IndexError, KeyError):
172 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000173 items = []
174 for name, lineno in cl.methods.items():
175 items.append((lineno, name))
176 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000177 list = []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000178 for item, name in items:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000179 list.append(name)
180 return list
Guido van Rossum439c4671998-10-13 03:59:57 +0000181
Guido van Rossumec9cca71999-06-01 18:21:31 +0000182class MethodBrowserTreeItem(TreeItem):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000183
Guido van Rossumec9cca71999-06-01 18:21:31 +0000184 def __init__(self, name, cl, file):
185 self.name = name
186 self.cl = cl
187 self.file = file
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000188
Guido van Rossumec9cca71999-06-01 18:21:31 +0000189 def GetText(self):
190 return "def " + self.name + "(...)"
191
192 def GetIconName(self):
193 return "python" # XXX
194
195 def IsExpandable(self):
196 return 0
197
198 def OnDoubleClick(self):
199 if not os.path.exists(self.file):
200 return
201 edit = PyShell.flist.open(self.file)
202 edit.gotoline(self.cl.methods[self.name])
203
204def main():
205 try:
206 file = __file__
207 except NameError:
208 file = sys.argv[0]
209 if sys.argv[1:]:
210 file = sys.argv[1]
211 else:
212 file = sys.argv[0]
213 dir, file = os.path.split(file)
214 name = os.path.splitext(file)[0]
215 ClassBrowser(PyShell.flist, name, [dir])
216 if sys.stdin is sys.__stdin__:
217 mainloop()
218
219if __name__ == "__main__":
220 main()