blob: 0cdd645db3de5e0f54e7c079a068ca6136633354 [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
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000011"""
12
Guido van Rossume6fae1c1998-10-15 23:27:08 +000013import os
Guido van Rossumec9cca71999-06-01 18:21:31 +000014import sys
Guido van Rossum439c4671998-10-13 03:59:57 +000015import string
16import pyclbr
Guido van Rossumec9cca71999-06-01 18:21:31 +000017
Guido van Rossum4431b0f1999-06-10 15:19:14 +000018# XXX Patch pyclbr with dummies if it's vintage Python 1.5.2:
19if not hasattr(pyclbr, "readmodule_ex"):
20 pyclbr.readmodule_ex = pyclbr.readmodule
21if not hasattr(pyclbr, "Function"):
22 class Function(pyclbr.Class):
23 pass
24 pyclbr.Function = Function
25
Guido van Rossumec9cca71999-06-01 18:21:31 +000026import PyShell
Guido van Rossum504b0bf1999-01-02 21:28:54 +000027from WindowList import ListedToplevel
Guido van Rossumec9cca71999-06-01 18:21:31 +000028from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
Guido van Rossume6fae1c1998-10-15 23:27:08 +000029
Guido van Rossum439c4671998-10-13 03:59:57 +000030class ClassBrowser:
Guido van Rossum504b0bf1999-01-02 21:28:54 +000031
Guido van Rossumec9cca71999-06-01 18:21:31 +000032 def __init__(self, flist, name, path):
33 self.name = name
34 self.file = os.path.join(path[0], self.name + ".py")
35 self.init(flist)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000036
Guido van Rossumdc5066e1999-01-11 14:46:06 +000037 def close(self, event=None):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000038 self.top.destroy()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000039
Guido van Rossumec9cca71999-06-01 18:21:31 +000040 def init(self, flist):
Guido van Rossume6fae1c1998-10-15 23:27:08 +000041 self.flist = flist
Guido van Rossumec9cca71999-06-01 18:21:31 +000042 # reset pyclbr
43 pyclbr._modules.clear()
44 # create top
45 self.top = top = ListedToplevel(flist.root)
46 top.protocol("WM_DELETE_WINDOW", self.close)
47 top.bind("<Escape>", self.close)
48 self.settitle()
49 top.focus_set()
50 # create scrolled canvas
51 sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
52 sc.frame.pack(expand=1, fill="both")
53 item = self.rootnode()
54 node = TreeNode(sc.canvas, None, item)
55 node.update()
56 node.expand()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000057
Guido van Rossumec9cca71999-06-01 18:21:31 +000058 def settitle(self):
59 self.top.wm_title("Class Browser - " + self.name)
60 self.top.wm_iconname("Class Browser")
61
62 def rootnode(self):
63 return ModuleBrowserTreeItem(self.file)
64
65class ModuleBrowserTreeItem(TreeItem):
66
67 def __init__(self, file):
68 self.file = file
69
70 def GetText(self):
71 return os.path.basename(self.file)
72
73 def GetIconName(self):
74 return "python"
75
76 def GetSubList(self):
77 sublist = []
78 for name in self.listclasses():
79 item = ClassBrowserTreeItem(name, self.classes, self.file)
80 sublist.append(item)
81 return sublist
82
83 def OnDoubleClick(self):
84 if os.path.normcase(self.file[-3:]) != ".py":
85 return
86 if not os.path.exists(self.file):
87 return
88 PyShell.flist.open(self.file)
89
90 def IsExpandable(self):
91 return os.path.normcase(self.file[-3:]) == ".py"
92
93 def listclasses(self):
94 dir, file = os.path.split(self.file)
95 name, ext = os.path.splitext(file)
96 if os.path.normcase(ext) != ".py":
97 return []
98 try:
Guido van Rossum4431b0f1999-06-10 15:19:14 +000099 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000100 except ImportError, msg:
101 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000102 items = []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000103 self.classes = {}
104 for key, cl in dict.items():
105 if cl.module == name:
106 s = key
107 if cl.super:
108 supers = []
109 for sup in cl.super:
110 if type(sup) is type(''):
111 sname = sup
112 else:
113 sname = sup.name
114 if sup.module != cl.module:
115 sname = "%s.%s" % (sup.module, sname)
116 supers.append(sname)
117 s = s + "(%s)" % string.join(supers, ", ")
118 items.append((cl.lineno, s))
119 self.classes[s] = cl
Guido van Rossum439c4671998-10-13 03:59:57 +0000120 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000121 list = []
122 for item, s in items:
123 list.append(s)
124 return list
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000125
Guido van Rossumec9cca71999-06-01 18:21:31 +0000126class ClassBrowserTreeItem(TreeItem):
127
128 def __init__(self, name, classes, file):
129 self.name = name
130 self.classes = classes
131 self.file = file
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000132 try:
133 self.cl = self.classes[self.name]
134 except (IndexError, KeyError):
135 self.cl = None
136 self.isfunction = isinstance(self.cl, pyclbr.Function)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000137
138 def GetText(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000139 if self.isfunction:
140 return "def " + self.name + "(...)"
141 else:
142 return "class " + self.name
143
144 def GetIconName(self):
145 if self.isfunction:
146 return "python"
147 else:
148 return "folder"
Guido van Rossumec9cca71999-06-01 18:21:31 +0000149
150 def IsExpandable(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000151 if self.cl:
152 return not not self.cl.methods
Guido van Rossumec9cca71999-06-01 18:21:31 +0000153
154 def GetSubList(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000155 if not self.cl:
156 return []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000157 sublist = []
158 for name in self.listmethods():
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000159 item = MethodBrowserTreeItem(name, self.cl, self.file)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000160 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)
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000167 if hasattr(self.cl, 'lineno'):
168 lineno = self.cl.lineno
169 edit.gotoline(lineno)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000170
Guido van Rossumec9cca71999-06-01 18:21:31 +0000171 def listmethods(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000172 if not self.cl:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000173 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000174 items = []
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000175 for name, lineno in self.cl.methods.items():
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000176 items.append((lineno, name))
177 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000178 list = []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000179 for item, name in items:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000180 list.append(name)
181 return list
Guido van Rossum439c4671998-10-13 03:59:57 +0000182
Guido van Rossumec9cca71999-06-01 18:21:31 +0000183class MethodBrowserTreeItem(TreeItem):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000184
Guido van Rossumec9cca71999-06-01 18:21:31 +0000185 def __init__(self, name, cl, file):
186 self.name = name
187 self.cl = cl
188 self.file = file
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000189
Guido van Rossumec9cca71999-06-01 18:21:31 +0000190 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()