blob: 19f3b7e6003c11634b9e192cb1f21a555ca999a7 [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):
Guido van Rossumf801f3b2000-02-15 18:02:11 +000033 # XXX This API should change, if the file doesn't end in ".py"
34 # XXX the code here is bogus!
Guido van Rossumec9cca71999-06-01 18:21:31 +000035 self.name = name
36 self.file = os.path.join(path[0], self.name + ".py")
37 self.init(flist)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000038
Guido van Rossumdc5066e1999-01-11 14:46:06 +000039 def close(self, event=None):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000040 self.top.destroy()
Guido van Rossum5af0df51999-06-25 17:08:19 +000041 self.node.destroy()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000042
Guido van Rossumec9cca71999-06-01 18:21:31 +000043 def init(self, flist):
Guido van Rossume6fae1c1998-10-15 23:27:08 +000044 self.flist = flist
Guido van Rossumec9cca71999-06-01 18:21:31 +000045 # reset pyclbr
46 pyclbr._modules.clear()
47 # create top
48 self.top = top = ListedToplevel(flist.root)
49 top.protocol("WM_DELETE_WINDOW", self.close)
50 top.bind("<Escape>", self.close)
51 self.settitle()
52 top.focus_set()
53 # create scrolled canvas
54 sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
55 sc.frame.pack(expand=1, fill="both")
56 item = self.rootnode()
Guido van Rossum5af0df51999-06-25 17:08:19 +000057 self.node = node = TreeNode(sc.canvas, None, item)
Guido van Rossumec9cca71999-06-01 18:21:31 +000058 node.update()
59 node.expand()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000060
Guido van Rossumec9cca71999-06-01 18:21:31 +000061 def settitle(self):
62 self.top.wm_title("Class Browser - " + self.name)
63 self.top.wm_iconname("Class Browser")
64
65 def rootnode(self):
66 return ModuleBrowserTreeItem(self.file)
67
68class ModuleBrowserTreeItem(TreeItem):
69
70 def __init__(self, file):
71 self.file = file
72
73 def GetText(self):
74 return os.path.basename(self.file)
75
76 def GetIconName(self):
77 return "python"
78
79 def GetSubList(self):
80 sublist = []
81 for name in self.listclasses():
82 item = ClassBrowserTreeItem(name, self.classes, self.file)
83 sublist.append(item)
84 return sublist
85
86 def OnDoubleClick(self):
87 if os.path.normcase(self.file[-3:]) != ".py":
88 return
89 if not os.path.exists(self.file):
90 return
91 PyShell.flist.open(self.file)
92
93 def IsExpandable(self):
94 return os.path.normcase(self.file[-3:]) == ".py"
Tim Peters70c43782001-01-17 08:48:39 +000095
Guido van Rossumec9cca71999-06-01 18:21:31 +000096 def listclasses(self):
97 dir, file = os.path.split(self.file)
98 name, ext = os.path.splitext(file)
99 if os.path.normcase(ext) != ".py":
100 return []
101 try:
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000102 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000103 except ImportError, msg:
104 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000105 items = []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000106 self.classes = {}
107 for key, cl in dict.items():
108 if cl.module == name:
109 s = key
110 if cl.super:
111 supers = []
112 for sup in cl.super:
113 if type(sup) is type(''):
114 sname = sup
115 else:
116 sname = sup.name
117 if sup.module != cl.module:
118 sname = "%s.%s" % (sup.module, sname)
119 supers.append(sname)
120 s = s + "(%s)" % string.join(supers, ", ")
121 items.append((cl.lineno, s))
122 self.classes[s] = cl
Guido van Rossum439c4671998-10-13 03:59:57 +0000123 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000124 list = []
125 for item, s in items:
126 list.append(s)
127 return list
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000128
Guido van Rossumec9cca71999-06-01 18:21:31 +0000129class ClassBrowserTreeItem(TreeItem):
130
131 def __init__(self, name, classes, file):
132 self.name = name
133 self.classes = classes
134 self.file = file
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000135 try:
136 self.cl = self.classes[self.name]
137 except (IndexError, KeyError):
138 self.cl = None
139 self.isfunction = isinstance(self.cl, pyclbr.Function)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000140
141 def GetText(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000142 if self.isfunction:
143 return "def " + self.name + "(...)"
144 else:
145 return "class " + self.name
146
147 def GetIconName(self):
148 if self.isfunction:
149 return "python"
150 else:
151 return "folder"
Guido van Rossumec9cca71999-06-01 18:21:31 +0000152
153 def IsExpandable(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000154 if self.cl:
155 return not not self.cl.methods
Guido van Rossumec9cca71999-06-01 18:21:31 +0000156
157 def GetSubList(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000158 if not self.cl:
159 return []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000160 sublist = []
161 for name in self.listmethods():
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000162 item = MethodBrowserTreeItem(name, self.cl, self.file)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000163 sublist.append(item)
164 return sublist
165
166 def OnDoubleClick(self):
167 if not os.path.exists(self.file):
168 return
169 edit = PyShell.flist.open(self.file)
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000170 if hasattr(self.cl, 'lineno'):
171 lineno = self.cl.lineno
172 edit.gotoline(lineno)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000173
Guido van Rossumec9cca71999-06-01 18:21:31 +0000174 def listmethods(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000175 if not self.cl:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000176 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000177 items = []
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000178 for name, lineno in self.cl.methods.items():
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000179 items.append((lineno, name))
180 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000181 list = []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000182 for item, name in items:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000183 list.append(name)
184 return list
Guido van Rossum439c4671998-10-13 03:59:57 +0000185
Guido van Rossumec9cca71999-06-01 18:21:31 +0000186class MethodBrowserTreeItem(TreeItem):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000187
Guido van Rossumec9cca71999-06-01 18:21:31 +0000188 def __init__(self, name, cl, file):
189 self.name = name
190 self.cl = cl
191 self.file = file
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000192
Guido van Rossumec9cca71999-06-01 18:21:31 +0000193 def GetText(self):
194 return "def " + self.name + "(...)"
195
196 def GetIconName(self):
197 return "python" # XXX
198
199 def IsExpandable(self):
200 return 0
201
202 def OnDoubleClick(self):
203 if not os.path.exists(self.file):
204 return
205 edit = PyShell.flist.open(self.file)
206 edit.gotoline(self.cl.methods[self.name])
207
208def main():
209 try:
210 file = __file__
211 except NameError:
212 file = sys.argv[0]
213 if sys.argv[1:]:
214 file = sys.argv[1]
215 else:
216 file = sys.argv[0]
217 dir, file = os.path.split(file)
218 name = os.path.splitext(file)[0]
219 ClassBrowser(PyShell.flist, name, [dir])
220 if sys.stdin is sys.__stdin__:
221 mainloop()
222
223if __name__ == "__main__":
224 main()