blob: f01f249ed81295091e48bdb2ea42fc74dea3d337 [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 pyclbr
Guido van Rossumec9cca71999-06-01 18:21:31 +000016
17import PyShell
Guido van Rossum504b0bf1999-01-02 21:28:54 +000018from WindowList import ListedToplevel
Guido van Rossumec9cca71999-06-01 18:21:31 +000019from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
Guido van Rossume6fae1c1998-10-15 23:27:08 +000020
Guido van Rossum439c4671998-10-13 03:59:57 +000021class ClassBrowser:
Guido van Rossum504b0bf1999-01-02 21:28:54 +000022
Guido van Rossumec9cca71999-06-01 18:21:31 +000023 def __init__(self, flist, name, path):
Guido van Rossumf801f3b2000-02-15 18:02:11 +000024 # XXX This API should change, if the file doesn't end in ".py"
25 # XXX the code here is bogus!
Guido van Rossumec9cca71999-06-01 18:21:31 +000026 self.name = name
27 self.file = os.path.join(path[0], self.name + ".py")
28 self.init(flist)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000029
Guido van Rossumdc5066e1999-01-11 14:46:06 +000030 def close(self, event=None):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000031 self.top.destroy()
Guido van Rossum5af0df51999-06-25 17:08:19 +000032 self.node.destroy()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000033
Guido van Rossumec9cca71999-06-01 18:21:31 +000034 def init(self, flist):
Guido van Rossume6fae1c1998-10-15 23:27:08 +000035 self.flist = flist
Guido van Rossumec9cca71999-06-01 18:21:31 +000036 # 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()
Guido van Rossum5af0df51999-06-25 17:08:19 +000048 self.node = node = TreeNode(sc.canvas, None, item)
Guido van Rossumec9cca71999-06-01 18:21:31 +000049 node.update()
50 node.expand()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000051
Guido van Rossumec9cca71999-06-01 18:21:31 +000052 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"
Tim Peters70c43782001-01-17 08:48:39 +000086
Guido van Rossumec9cca71999-06-01 18:21:31 +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:
Guido van Rossum4431b0f1999-06-10 15:19:14 +000093 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
Guido van Rossumec9cca71999-06-01 18:21:31 +000094 except ImportError, msg:
95 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000096 items = []
Guido van Rossumec9cca71999-06-01 18:21:31 +000097 self.classes = {}
98 for key, cl in dict.items():
99 if cl.module == name:
100 s = key
Raymond Hettinger18acea72003-01-18 22:53:36 +0000101 if hasattr(cl, "super") and cl.super:
Guido van Rossumec9cca71999-06-01 18:21:31 +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)
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000111 s = s + "(%s)" % ", ".join(supers)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000112 items.append((cl.lineno, s))
113 self.classes[s] = cl
Guido van Rossum439c4671998-10-13 03:59:57 +0000114 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000115 list = []
116 for item, s in items:
117 list.append(s)
118 return list
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000119
Guido van Rossumec9cca71999-06-01 18:21:31 +0000120class ClassBrowserTreeItem(TreeItem):
121
122 def __init__(self, name, classes, file):
123 self.name = name
124 self.classes = classes
125 self.file = file
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000126 try:
127 self.cl = self.classes[self.name]
128 except (IndexError, KeyError):
129 self.cl = None
130 self.isfunction = isinstance(self.cl, pyclbr.Function)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000131
132 def GetText(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000133 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"
Guido van Rossumec9cca71999-06-01 18:21:31 +0000143
144 def IsExpandable(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000145 if self.cl:
146 return not not self.cl.methods
Guido van Rossumec9cca71999-06-01 18:21:31 +0000147
148 def GetSubList(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000149 if not self.cl:
150 return []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000151 sublist = []
152 for name in self.listmethods():
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000153 item = MethodBrowserTreeItem(name, self.cl, self.file)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000154 sublist.append(item)
155 return sublist
156
157 def OnDoubleClick(self):
158 if not os.path.exists(self.file):
159 return
160 edit = PyShell.flist.open(self.file)
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000161 if hasattr(self.cl, 'lineno'):
162 lineno = self.cl.lineno
163 edit.gotoline(lineno)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000164
Guido van Rossumec9cca71999-06-01 18:21:31 +0000165 def listmethods(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000166 if not self.cl:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000167 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000168 items = []
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000169 for name, lineno in self.cl.methods.items():
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000170 items.append((lineno, name))
171 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000172 list = []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000173 for item, name in items:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000174 list.append(name)
175 return list
Guido van Rossum439c4671998-10-13 03:59:57 +0000176
Guido van Rossumec9cca71999-06-01 18:21:31 +0000177class MethodBrowserTreeItem(TreeItem):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000178
Guido van Rossumec9cca71999-06-01 18:21:31 +0000179 def __init__(self, name, cl, file):
180 self.name = name
181 self.cl = cl
182 self.file = file
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000183
Guido van Rossumec9cca71999-06-01 18:21:31 +0000184 def GetText(self):
185 return "def " + self.name + "(...)"
186
187 def GetIconName(self):
188 return "python" # XXX
189
190 def IsExpandable(self):
191 return 0
192
193 def OnDoubleClick(self):
194 if not os.path.exists(self.file):
195 return
196 edit = PyShell.flist.open(self.file)
197 edit.gotoline(self.cl.methods[self.name])
198
199def main():
200 try:
201 file = __file__
202 except NameError:
203 file = sys.argv[0]
204 if sys.argv[1:]:
205 file = sys.argv[1]
206 else:
207 file = sys.argv[0]
208 dir, file = os.path.split(file)
209 name = os.path.splitext(file)[0]
210 ClassBrowser(PyShell.flist, name, [dir])
211 if sys.stdin is sys.__stdin__:
212 mainloop()
213
214if __name__ == "__main__":
215 main()