blob: 8db037aff642562b01ab27dbb843dc71490fc3ac [file] [log] [blame]
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +00001"""Primitive class browser.
2
3XXX TO DO:
4
5- generalize the scrolling listbox with some behavior into a base class
6- add popup menu with more options (e.g. doc strings, base classes, imports)
7- show function argument list (have to do pattern matching on source)
8- should the classes and methods lists also be in the module's menu bar?
9
10"""
11
Guido van Rossum439c4671998-10-13 03:59:57 +000012import string
13import pyclbr
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000014from Tkinter import *
15import tkMessageBox
Guido van Rossum439c4671998-10-13 03:59:57 +000016
17class ClassBrowser:
18
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000019 def __init__(self, flist, name):
20 root = flist.root
Guido van Rossum439c4671998-10-13 03:59:57 +000021 try:
22 dict = pyclbr.readmodule(name)
23 except ImportError, msg:
24 tkMessageBox.showerror("Import error", str(msg), parent=root)
25 return
26 if not dict:
27 tkMessageBox.showerror("Nothing to browse",
28 "Module %s defines no classes" % name, parent=root)
29 return
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000030 self.flist = flist
31 self.dict = dict
Guido van Rossum439c4671998-10-13 03:59:57 +000032 self.root = root
33 self.top = top = Toplevel(root)
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000034 self.top.protocol("WM_DELETE_WINDOW", self.close)
35 self.leftframe = leftframe = Frame(top)
36 self.leftframe.pack(side="left", fill="both", expand=1)
37 top.wm_title("Class browser")
Guido van Rossum439c4671998-10-13 03:59:57 +000038 # Create help label
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000039 self.helplabel = Label(leftframe,
Guido van Rossum439c4671998-10-13 03:59:57 +000040 text="Classes in module %s" % name,
41 borderwidth=2, relief="groove")
42 self.helplabel.pack(fill="x")
43 # Create top frame, with scrollbar and listbox
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000044 self.topframe = Frame(leftframe)
Guido van Rossum439c4671998-10-13 03:59:57 +000045 self.topframe.pack(fill="both", expand=1)
46 self.vbar = Scrollbar(self.topframe, name="vbar")
47 self.vbar.pack(side="right", fill="y")
48 self.listbox = Listbox(self.topframe, exportselection=0,
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000049 takefocus=1, width=30)
Guido van Rossum439c4671998-10-13 03:59:57 +000050 self.listbox.pack(expand=1, fill="both")
51 # Tie listbox and scrollbar together
52 self.vbar["command"] = self.listbox.yview
53 self.listbox["yscrollcommand"] = self.vbar.set
54 # Bind events to the list box
55 self.listbox.bind("<ButtonRelease-1>", self.click_event)
56 self.listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
57 ##self.listbox.bind("<ButtonPress-3>", self.popup_event)
58 self.listbox.bind("<Key-Up>", self.up_event)
59 self.listbox.bind("<Key-Down>", self.down_event)
60 # Load the classes
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000061 self.loadclasses(dict, name)
62
63 def close(self):
64 self.top.destroy()
Guido van Rossum439c4671998-10-13 03:59:57 +000065
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000066 def loadclasses(self, dict, module):
67 items = []
68 for key, value in dict.items():
69 if value.module == module:
70 items.append((value.lineno, key, value))
Guido van Rossum439c4671998-10-13 03:59:57 +000071 items.sort()
72 l = self.listbox
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000073 for lineno, key, value in items:
Guido van Rossum439c4671998-10-13 03:59:57 +000074 s = key
75 if value.super:
76 super = []
77 for sup in value.super:
78 name = sup.name
79 if sup.module != value.module:
80 name = "%s.%s" % (sup.module, name)
81 super.append(name)
82 s = s + "(%s)" % string.join(super, ", ")
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +000083 l.insert("end", s)
84 l.focus_set()
85 l.selection_clear(0, "end")
86 if self.botframe:
87 self.botframe.destroy()
88 self.botframe = None
89 self.methodviewer = None
90
91 def click_event(self, event):
92 self.listbox.activate("@%d,%d" % (event.x, event.y))
93 index = self.listbox.index("active")
94 self.show_methods(index)
95
96 def double_click_event(self, event):
97 self.listbox.activate("@%d,%d" % (event.x, event.y))
98 index = self.listbox.index("active")
99 self.show_source(index)
100
101 def up_event(self, event):
102 index = self.listbox.index("active") - 1
103 if index < 0:
104 self.top.bell()
105 return "break"
106 self.show_methods(index)
107 return "break"
108
109 def down_event(self, event):
110 index = self.listbox.index("active") + 1
111 if index >= self.listbox.index("end"):
112 self.top.bell()
113 return "break"
114 self.show_methods(index)
115 return "break"
116
117 def show_source(self, index):
118 name = self.listbox.get(index)
119 i = string.find(name, '(')
120 if i >= 0:
121 name = name[:i]
122 cl = self.dict[name]
123 edit = self.flist.open(cl.file)
124 edit.gotoline(cl.lineno)
125
126 botframe = None
127 methodviewer = None
128
129 def show_methods(self, index):
130 self.listbox.selection_clear(0, "end")
131 self.listbox.selection_set(index)
132 self.listbox.activate(index)
133 self.listbox.see(index)
134 self.listbox.focus_set()
135 name = self.listbox.get(index)
136 i = string.find(name, '(')
137 if i >= 0:
138 name = name[:i]
139 cl = self.dict[name]
140 if not self.botframe:
141 self.botframe = Frame(self.top)
142 self.botframe.pack(expand=1, fill="both")
143 if not self.methodviewer:
144 self.methodviewer = MethodViewer(self.botframe, self.flist)
145 self.methodviewer.loadmethods(cl)
146
147class MethodViewer:
148
149 # XXX There's a pattern emerging here...
150
151 def __init__(self, frame, flist):
152 self.frame = frame
153 self.flist = flist
154 # Create help label
155 self.helplabel = Label(frame,
156 text="Methods", borderwidth=2, relief="groove")
157 self.helplabel.pack(fill="x")
158 # Create top frame, with scrollbar and listbox
159 self.topframe = Frame(frame)
160 self.topframe.pack(fill="both", expand=1)
161 self.vbar = Scrollbar(self.topframe, name="vbar")
162 self.vbar.pack(side="right", fill="y")
163 self.listbox = Listbox(self.topframe, exportselection=0,
164 takefocus=1, width=30)
165 self.listbox.pack(expand=1, fill="both")
166 # Tie listbox and scrollbar together
167 self.vbar["command"] = self.listbox.yview
168 self.listbox["yscrollcommand"] = self.vbar.set
169 # Bind events to the list box
170 self.listbox.bind("<ButtonRelease-1>", self.click_event)
171 self.listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
172 ##self.listbox.bind("<ButtonPress-3>", self.popup_event)
173 self.listbox.bind("<Key-Up>", self.up_event)
174 self.listbox.bind("<Key-Down>", self.down_event)
175
176 classinfo = None
177
178 def loadmethods(self, cl):
179 self.classinfo = cl
180 self.helplabel.config(text="Methods of class %s" % cl.name)
181 l = self.listbox
182 l.delete(0, "end")
183 l.selection_clear(0, "end")
184 items = []
185 for name, lineno in cl.methods.items():
186 items.append((lineno, name))
187 items.sort()
188 for item, name in items:
189 l.insert("end", name)
Guido van Rossum439c4671998-10-13 03:59:57 +0000190
191 def click_event(self, event):
192 pass
193
194 def double_click_event(self, event):
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000195 self.listbox.activate("@%d,%d" % (event.x, event.y))
196 index = self.listbox.index("active")
197 self.show_source(index)
Guido van Rossum439c4671998-10-13 03:59:57 +0000198
199 def up_event(self, event):
200 pass
201
202 def down_event(self, event):
203 pass
Guido van Rossum7f5c9ef1998-10-13 16:31:03 +0000204
205 def show_source(self, index):
206 name = self.listbox.get(index)
207 i = string.find(name, '(')
208 if i >= 0:
209 name = name[:i]
210 edit = self.flist.open(self.classinfo.file)
211 edit.gotoline(self.classinfo.methods[name])
212