Credit Nir Aides for r77288
[python.git] / Lib / idlelib / ClassBrowser.py
blobe5a60a51341f2cb8135949a5201eda295e9a86a3
1 """Class browser.
3 XXX TO DO:
5 - reparse when source changed (maybe just a button would be OK?)
6 (or recheck on window popup)
7 - add popup menu with more options (e.g. doc strings, base classes, imports)
8 - show function argument list? (have to do pattern matching on source)
9 - should the classes and methods lists also be in the module's menu bar?
10 - add base classes to class browser tree
11 """
13 import os
14 import sys
15 import pyclbr
17 import PyShell
18 from WindowList import ListedToplevel
19 from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
20 from configHandler import idleConf
22 class ClassBrowser:
24 def __init__(self, flist, name, path):
25 # XXX This API should change, if the file doesn't end in ".py"
26 # XXX the code here is bogus!
27 self.name = name
28 self.file = os.path.join(path[0], self.name + ".py")
29 self.init(flist)
31 def close(self, event=None):
32 self.top.destroy()
33 self.node.destroy()
35 def init(self, flist):
36 self.flist = flist
37 # reset pyclbr
38 pyclbr._modules.clear()
39 # create top
40 self.top = top = ListedToplevel(flist.root)
41 top.protocol("WM_DELETE_WINDOW", self.close)
42 top.bind("<Escape>", self.close)
43 self.settitle()
44 top.focus_set()
45 # create scrolled canvas
46 theme = idleConf.GetOption('main','Theme','name')
47 background = idleConf.GetHighlight(theme, 'normal')['background']
48 sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
49 sc.frame.pack(expand=1, fill="both")
50 item = self.rootnode()
51 self.node = node = TreeNode(sc.canvas, None, item)
52 node.update()
53 node.expand()
55 def settitle(self):
56 self.top.wm_title("Class Browser - " + self.name)
57 self.top.wm_iconname("Class Browser")
59 def rootnode(self):
60 return ModuleBrowserTreeItem(self.file)
62 class ModuleBrowserTreeItem(TreeItem):
64 def __init__(self, file):
65 self.file = file
67 def GetText(self):
68 return os.path.basename(self.file)
70 def GetIconName(self):
71 return "python"
73 def GetSubList(self):
74 sublist = []
75 for name in self.listclasses():
76 item = ClassBrowserTreeItem(name, self.classes, self.file)
77 sublist.append(item)
78 return sublist
80 def OnDoubleClick(self):
81 if os.path.normcase(self.file[-3:]) != ".py":
82 return
83 if not os.path.exists(self.file):
84 return
85 PyShell.flist.open(self.file)
87 def IsExpandable(self):
88 return os.path.normcase(self.file[-3:]) == ".py"
90 def listclasses(self):
91 dir, file = os.path.split(self.file)
92 name, ext = os.path.splitext(file)
93 if os.path.normcase(ext) != ".py":
94 return []
95 try:
96 dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
97 except ImportError, msg:
98 return []
99 items = []
100 self.classes = {}
101 for key, cl in dict.items():
102 if cl.module == name:
103 s = key
104 if hasattr(cl, 'super') and cl.super:
105 supers = []
106 for sup in cl.super:
107 if type(sup) is type(''):
108 sname = sup
109 else:
110 sname = sup.name
111 if sup.module != cl.module:
112 sname = "%s.%s" % (sup.module, sname)
113 supers.append(sname)
114 s = s + "(%s)" % ", ".join(supers)
115 items.append((cl.lineno, s))
116 self.classes[s] = cl
117 items.sort()
118 list = []
119 for item, s in items:
120 list.append(s)
121 return list
123 class ClassBrowserTreeItem(TreeItem):
125 def __init__(self, name, classes, file):
126 self.name = name
127 self.classes = classes
128 self.file = file
129 try:
130 self.cl = self.classes[self.name]
131 except (IndexError, KeyError):
132 self.cl = None
133 self.isfunction = isinstance(self.cl, pyclbr.Function)
135 def GetText(self):
136 if self.isfunction:
137 return "def " + self.name + "(...)"
138 else:
139 return "class " + self.name
141 def GetIconName(self):
142 if self.isfunction:
143 return "python"
144 else:
145 return "folder"
147 def IsExpandable(self):
148 if self.cl:
149 try:
150 return not not self.cl.methods
151 except AttributeError:
152 return False
154 def GetSubList(self):
155 if not self.cl:
156 return []
157 sublist = []
158 for name in self.listmethods():
159 item = MethodBrowserTreeItem(name, self.cl, self.file)
160 sublist.append(item)
161 return sublist
163 def OnDoubleClick(self):
164 if not os.path.exists(self.file):
165 return
166 edit = PyShell.flist.open(self.file)
167 if hasattr(self.cl, 'lineno'):
168 lineno = self.cl.lineno
169 edit.gotoline(lineno)
171 def listmethods(self):
172 if not self.cl:
173 return []
174 items = []
175 for name, lineno in self.cl.methods.items():
176 items.append((lineno, name))
177 items.sort()
178 list = []
179 for item, name in items:
180 list.append(name)
181 return list
183 class MethodBrowserTreeItem(TreeItem):
185 def __init__(self, name, cl, file):
186 self.name = name
187 self.cl = cl
188 self.file = file
190 def GetText(self):
191 return "def " + self.name + "(...)"
193 def GetIconName(self):
194 return "python" # XXX
196 def IsExpandable(self):
197 return 0
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])
205 def 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()
220 if __name__ == "__main__":
221 main()