blob: 6e4b2a32eeab9493f69e5d581bc004184c2398db [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
Guido van Rossum4431b0f1999-06-10 15:19:14 +000017# XXX Patch pyclbr with dummies if it's vintage Python 1.5.2:
18if not hasattr(pyclbr, "readmodule_ex"):
19 pyclbr.readmodule_ex = pyclbr.readmodule
20if not hasattr(pyclbr, "Function"):
21 class Function(pyclbr.Class):
22 pass
23 pyclbr.Function = Function
24
Guido van Rossumec9cca71999-06-01 18:21:31 +000025import PyShell
Guido van Rossum504b0bf1999-01-02 21:28:54 +000026from WindowList import ListedToplevel
Guido van Rossumec9cca71999-06-01 18:21:31 +000027from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
Guido van Rossume6fae1c1998-10-15 23:27:08 +000028
Guido van Rossum439c4671998-10-13 03:59:57 +000029class ClassBrowser:
Guido van Rossum504b0bf1999-01-02 21:28:54 +000030
Guido van Rossumec9cca71999-06-01 18:21:31 +000031 def __init__(self, flist, name, path):
Guido van Rossumf801f3b2000-02-15 18:02:11 +000032 # XXX This API should change, if the file doesn't end in ".py"
33 # XXX the code here is bogus!
Guido van Rossumec9cca71999-06-01 18:21:31 +000034 self.name = name
35 self.file = os.path.join(path[0], self.name + ".py")
36 self.init(flist)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000037
Guido van Rossumdc5066e1999-01-11 14:46:06 +000038 def close(self, event=None):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000039 self.top.destroy()
Guido van Rossum5af0df51999-06-25 17:08:19 +000040 self.node.destroy()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000041
Guido van Rossumec9cca71999-06-01 18:21:31 +000042 def init(self, flist):
Guido van Rossume6fae1c1998-10-15 23:27:08 +000043 self.flist = flist
Guido van Rossumec9cca71999-06-01 18:21:31 +000044 # reset pyclbr
45 pyclbr._modules.clear()
46 # create top
47 self.top = top = ListedToplevel(flist.root)
48 top.protocol("WM_DELETE_WINDOW", self.close)
49 top.bind("<Escape>", self.close)
50 self.settitle()
51 top.focus_set()
52 # create scrolled canvas
53 sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
54 sc.frame.pack(expand=1, fill="both")
55 item = self.rootnode()
Guido van Rossum5af0df51999-06-25 17:08:19 +000056 self.node = node = TreeNode(sc.canvas, None, item)
Guido van Rossumec9cca71999-06-01 18:21:31 +000057 node.update()
58 node.expand()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000059
Guido van Rossumec9cca71999-06-01 18:21:31 +000060 def settitle(self):
61 self.top.wm_title("Class Browser - " + self.name)
62 self.top.wm_iconname("Class Browser")
63
64 def rootnode(self):
65 return ModuleBrowserTreeItem(self.file)
66
67class ModuleBrowserTreeItem(TreeItem):
68
69 def __init__(self, file):
70 self.file = file
71
72 def GetText(self):
73 return os.path.basename(self.file)
74
75 def GetIconName(self):
76 return "python"
77
78 def GetSubList(self):
79 sublist = []
80 for name in self.listclasses():
81 item = ClassBrowserTreeItem(name, self.classes, self.file)
82 sublist.append(item)
83 return sublist
84
85 def OnDoubleClick(self):
86 if os.path.normcase(self.file[-3:]) != ".py":
87 return
88 if not os.path.exists(self.file):
89 return
90 PyShell.flist.open(self.file)
91
92 def IsExpandable(self):
93 return os.path.normcase(self.file[-3:]) == ".py"
Tim Peters70c43782001-01-17 08:48:39 +000094
Guido van Rossumec9cca71999-06-01 18:21:31 +000095 def listclasses(self):
96 dir, file = os.path.split(self.file)
97 name, ext = os.path.splitext(file)
98 if os.path.normcase(ext) != ".py":
99 return []
100 try:
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000101 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000102 except ImportError, msg:
103 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000104 items = []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000105 self.classes = {}
106 for key, cl in dict.items():
107 if cl.module == name:
108 s = key
109 if cl.super:
110 supers = []
111 for sup in cl.super:
112 if type(sup) is type(''):
113 sname = sup
114 else:
115 sname = sup.name
116 if sup.module != cl.module:
117 sname = "%s.%s" % (sup.module, sname)
118 supers.append(sname)
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000119 s = s + "(%s)" % ", ".join(supers)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000120 items.append((cl.lineno, s))
121 self.classes[s] = cl
Guido van Rossum439c4671998-10-13 03:59:57 +0000122 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000123 list = []
124 for item, s in items:
125 list.append(s)
126 return list
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000127
Guido van Rossumec9cca71999-06-01 18:21:31 +0000128class ClassBrowserTreeItem(TreeItem):
129
130 def __init__(self, name, classes, file):
131 self.name = name
132 self.classes = classes
133 self.file = file
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000134 try:
135 self.cl = self.classes[self.name]
136 except (IndexError, KeyError):
137 self.cl = None
138 self.isfunction = isinstance(self.cl, pyclbr.Function)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000139
140 def GetText(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000141 if self.isfunction:
142 return "def " + self.name + "(...)"
143 else:
144 return "class " + self.name
145
146 def GetIconName(self):
147 if self.isfunction:
148 return "python"
149 else:
150 return "folder"
Guido van Rossumec9cca71999-06-01 18:21:31 +0000151
152 def IsExpandable(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000153 if self.cl:
154 return not not self.cl.methods
Guido van Rossumec9cca71999-06-01 18:21:31 +0000155
156 def GetSubList(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000157 if not self.cl:
158 return []
Guido van Rossumec9cca71999-06-01 18:21:31 +0000159 sublist = []
160 for name in self.listmethods():
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000161 item = MethodBrowserTreeItem(name, self.cl, self.file)
Guido van Rossumec9cca71999-06-01 18:21:31 +0000162 sublist.append(item)
163 return sublist
164
165 def OnDoubleClick(self):
166 if not os.path.exists(self.file):
167 return
168 edit = PyShell.flist.open(self.file)
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000169 if hasattr(self.cl, 'lineno'):
170 lineno = self.cl.lineno
171 edit.gotoline(lineno)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000172
Guido van Rossumec9cca71999-06-01 18:21:31 +0000173 def listmethods(self):
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000174 if not self.cl:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000175 return []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000176 items = []
Guido van Rossum4431b0f1999-06-10 15:19:14 +0000177 for name, lineno in self.cl.methods.items():
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000178 items.append((lineno, name))
179 items.sort()
Guido van Rossumec9cca71999-06-01 18:21:31 +0000180 list = []
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000181 for item, name in items:
Guido van Rossumec9cca71999-06-01 18:21:31 +0000182 list.append(name)
183 return list
Guido van Rossum439c4671998-10-13 03:59:57 +0000184
Guido van Rossumec9cca71999-06-01 18:21:31 +0000185class MethodBrowserTreeItem(TreeItem):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000186
Guido van Rossumec9cca71999-06-01 18:21:31 +0000187 def __init__(self, name, cl, file):
188 self.name = name
189 self.cl = cl
190 self.file = file
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000191
Guido van Rossumec9cca71999-06-01 18:21:31 +0000192 def GetText(self):
193 return "def " + self.name + "(...)"
194
195 def GetIconName(self):
196 return "python" # XXX
197
198 def IsExpandable(self):
199 return 0
200
201 def OnDoubleClick(self):
202 if not os.path.exists(self.file):
203 return
204 edit = PyShell.flist.open(self.file)
205 edit.gotoline(self.cl.methods[self.name])
206
207def main():
208 try:
209 file = __file__
210 except NameError:
211 file = sys.argv[0]
212 if sys.argv[1:]:
213 file = sys.argv[1]
214 else:
215 file = sys.argv[0]
216 dir, file = os.path.split(file)
217 name = os.path.splitext(file)[0]
218 ClassBrowser(PyShell.flist, name, [dir])
219 if sys.stdin is sys.__stdin__:
220 mainloop()
221
222if __name__ == "__main__":
223 main()