]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | """Filling is the gui tree control through which a user can navigate |
2 | the local namespace or any object.""" | |
1fded56b | 3 | |
d14a1e28 RD |
4 | __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" |
5 | __cvsid__ = "$Id$" | |
6 | __revision__ = "$Revision$"[11:-2] | |
1fded56b | 7 | |
d14a1e28 RD |
8 | import wx |
9 | ||
10 | import dispatcher | |
11 | import editwindow | |
12 | import inspect | |
13 | import introspect | |
14 | import keyword | |
15 | import sys | |
16 | import types | |
17 | from version import VERSION | |
18 | ||
d14a1e28 RD |
19 | |
20 | COMMONTYPES = [getattr(types, t) for t in dir(types) \ | |
21 | if not t.startswith('_') \ | |
22 | and t not in ('ClassType', 'InstanceType', 'ModuleType')] | |
23 | ||
24 | DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', | |
25 | 'FunctionType', 'GeneratorType', 'InstanceType', | |
26 | 'LambdaType', 'MethodType', 'ModuleType', | |
27 | 'UnboundMethodType', 'method-wrapper') | |
28 | ||
29 | SIMPLETYPES = [getattr(types, t) for t in dir(types) \ | |
30 | if not t.startswith('_') and t not in DOCTYPES] | |
31 | ||
32 | del t | |
33 | ||
34 | try: | |
35 | COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x. | |
36 | except AttributeError: | |
37 | pass | |
38 | ||
39 | ||
40 | class FillingTree(wx.TreeCtrl): | |
41 | """FillingTree based on TreeCtrl.""" | |
42 | ||
43 | name = 'Filling Tree' | |
44 | revision = __revision__ | |
45 | ||
46 | def __init__(self, parent, id=-1, pos=wx.DefaultPosition, | |
47 | size=wx.DefaultSize, style=wx.TR_DEFAULT_STYLE, | |
48 | rootObject=None, rootLabel=None, rootIsNamespace=False, | |
49 | static=False): | |
50 | """Create FillingTree instance.""" | |
51 | wx.TreeCtrl.__init__(self, parent, id, pos, size, style) | |
52 | self.rootIsNamespace = rootIsNamespace | |
53 | import __main__ | |
54 | if rootObject is None: | |
55 | rootObject = __main__.__dict__ | |
56 | self.rootIsNamespace = True | |
57 | if rootObject is __main__.__dict__ and rootLabel is None: | |
58 | rootLabel = 'locals()' | |
59 | if not rootLabel: | |
60 | rootLabel = 'Ingredients' | |
61 | rootData = wx.TreeItemData(rootObject) | |
62 | self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData) | |
63 | self.SetItemHasChildren(self.root, self.objHasChildren(rootObject)) | |
64 | wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding) | |
65 | wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed) | |
66 | wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) | |
67 | wx.EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) | |
68 | if not static: | |
69 | dispatcher.connect(receiver=self.push, signal='Interpreter.push') | |
70 | ||
71 | def push(self, command, more): | |
72 | """Receiver for Interpreter.push signal.""" | |
73 | self.display() | |
74 | ||
75 | def OnItemExpanding(self, event): | |
76 | """Add children to the item.""" | |
77 | busy = wx.BusyCursor() | |
78 | item = event.GetItem() | |
79 | if self.IsExpanded(item): | |
80 | return | |
81 | self.addChildren(item) | |
82 | # self.SelectItem(item) | |
83 | ||
84 | def OnItemCollapsed(self, event): | |
85 | """Remove all children from the item.""" | |
86 | busy = wx.BusyCursor() | |
87 | item = event.GetItem() | |
88 | # self.CollapseAndReset(item) | |
89 | # self.DeleteChildren(item) | |
90 | # self.SelectItem(item) | |
91 | ||
92 | def OnSelChanged(self, event): | |
93 | """Display information about the item.""" | |
94 | busy = wx.BusyCursor() | |
95 | self.item = event.GetItem() | |
96 | self.display() | |
97 | ||
98 | def OnItemActivated(self, event): | |
99 | """Launch a DirFrame.""" | |
100 | item = event.GetItem() | |
101 | text = self.getFullName(item) | |
102 | obj = self.GetPyData(item) | |
103 | frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj, | |
104 | rootLabel=text, rootIsNamespace=False) | |
105 | frame.Show() | |
106 | ||
107 | def objHasChildren(self, obj): | |
108 | """Return true if object has children.""" | |
109 | if self.objGetChildren(obj): | |
110 | return True | |
111 | else: | |
112 | return False | |
113 | ||
114 | def objGetChildren(self, obj): | |
115 | """Return dictionary with attributes or contents of object.""" | |
116 | busy = wx.BusyCursor() | |
117 | otype = type(obj) | |
118 | if otype is types.DictType \ | |
119 | or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'): | |
120 | return obj | |
121 | d = {} | |
122 | if otype is types.ListType or otype is types.TupleType: | |
123 | for n in range(len(obj)): | |
124 | key = '[' + str(n) + ']' | |
125 | d[key] = obj[n] | |
126 | if otype not in COMMONTYPES: | |
127 | for key in introspect.getAttributeNames(obj): | |
128 | # Believe it or not, some attributes can disappear, | |
129 | # such as the exc_traceback attribute of the sys | |
130 | # module. So this is nested in a try block. | |
131 | try: | |
132 | d[key] = getattr(obj, key) | |
133 | except: | |
134 | pass | |
135 | return d | |
136 | ||
137 | def addChildren(self, item): | |
138 | self.DeleteChildren(item) | |
139 | obj = self.GetPyData(item) | |
140 | children = self.objGetChildren(obj) | |
141 | if not children: | |
142 | return | |
143 | keys = children.keys() | |
144 | keys.sort(lambda x, y: cmp(str(x).lower(), str(y).lower())) | |
145 | for key in keys: | |
146 | itemtext = str(key) | |
147 | # Show string dictionary items with single quotes, except | |
148 | # for the first level of items, if they represent a | |
149 | # namespace. | |
150 | if type(obj) is types.DictType \ | |
151 | and type(key) is types.StringType \ | |
152 | and (item != self.root \ | |
153 | or (item == self.root and not self.rootIsNamespace)): | |
154 | itemtext = repr(key) | |
155 | child = children[key] | |
156 | data = wx.TreeItemData(child) | |
157 | branch = self.AppendItem(parent=item, text=itemtext, data=data) | |
158 | self.SetItemHasChildren(branch, self.objHasChildren(child)) | |
159 | ||
160 | def display(self): | |
161 | item = self.item | |
162 | if self.IsExpanded(item): | |
163 | self.addChildren(item) | |
164 | self.setText('') | |
165 | obj = self.GetPyData(item) | |
166 | if wx.Platform == '__WXMSW__': | |
167 | if obj is None: # Windows bug fix. | |
168 | return | |
169 | self.SetItemHasChildren(item, self.objHasChildren(obj)) | |
170 | otype = type(obj) | |
171 | text = '' | |
172 | text += self.getFullName(item) | |
173 | text += '\n\nType: ' + str(otype) | |
174 | try: | |
175 | value = str(obj) | |
176 | except: | |
177 | value = '' | |
178 | if otype is types.StringType or otype is types.UnicodeType: | |
179 | value = repr(obj) | |
180 | text += '\n\nValue: ' + value | |
181 | if otype not in SIMPLETYPES: | |
182 | try: | |
183 | text += '\n\nDocstring:\n\n"""' + \ | |
184 | inspect.getdoc(obj).strip() + '"""' | |
185 | except: | |
186 | pass | |
187 | if otype is types.InstanceType: | |
188 | try: | |
189 | text += '\n\nClass Definition:\n\n' + \ | |
190 | inspect.getsource(obj.__class__) | |
191 | except: | |
192 | pass | |
193 | else: | |
194 | try: | |
195 | text += '\n\nSource Code:\n\n' + \ | |
196 | inspect.getsource(obj) | |
197 | except: | |
198 | pass | |
199 | self.setText(text) | |
200 | ||
201 | def getFullName(self, item, partial=''): | |
202 | """Return a syntactically proper name for item.""" | |
203 | name = self.GetItemText(item) | |
204 | parent = None | |
205 | obj = None | |
206 | if item != self.root: | |
207 | parent = self.GetItemParent(item) | |
208 | obj = self.GetPyData(parent) | |
209 | # Apply dictionary syntax to dictionary items, except the root | |
210 | # and first level children of a namepace. | |
211 | if (type(obj) is types.DictType \ | |
212 | or str(type(obj))[17:23] == 'BTrees' \ | |
213 | and hasattr(obj, 'keys')) \ | |
214 | and ((item != self.root and parent != self.root) \ | |
215 | or (parent == self.root and not self.rootIsNamespace)): | |
216 | name = '[' + name + ']' | |
217 | # Apply dot syntax to multipart names. | |
218 | if partial: | |
219 | if partial[0] == '[': | |
220 | name += partial | |
221 | else: | |
222 | name += '.' + partial | |
223 | # Repeat for everything but the root item | |
224 | # and first level children of a namespace. | |
225 | if (item != self.root and parent != self.root) \ | |
226 | or (parent == self.root and not self.rootIsNamespace): | |
227 | name = self.getFullName(parent, partial=name) | |
228 | return name | |
229 | ||
230 | def setText(self, text): | |
231 | """Display information about the current selection.""" | |
232 | ||
233 | # This method will likely be replaced by the enclosing app to | |
234 | # do something more interesting, like write to a text control. | |
235 | print text | |
236 | ||
237 | def setStatusText(self, text): | |
238 | """Display status information.""" | |
239 | ||
240 | # This method will likely be replaced by the enclosing app to | |
241 | # do something more interesting, like write to a status bar. | |
242 | print text | |
243 | ||
244 | ||
245 | class FillingText(editwindow.EditWindow): | |
246 | """FillingText based on StyledTextCtrl.""" | |
247 | ||
248 | name = 'Filling Text' | |
249 | revision = __revision__ | |
250 | ||
251 | def __init__(self, parent, id=-1, pos=wx.DefaultPosition, | |
252 | size=wx.DefaultSize, style=wx.CLIP_CHILDREN, | |
253 | static=False): | |
254 | """Create FillingText instance.""" | |
255 | editwindow.EditWindow.__init__(self, parent, id, pos, size, style) | |
256 | # Configure various defaults and user preferences. | |
257 | self.SetReadOnly(True) | |
258 | self.SetWrapMode(True) | |
259 | self.SetMarginWidth(1, 0) | |
260 | if not static: | |
261 | dispatcher.connect(receiver=self.push, signal='Interpreter.push') | |
262 | ||
263 | def push(self, command, more): | |
264 | """Receiver for Interpreter.push signal.""" | |
265 | self.Refresh() | |
266 | ||
267 | def SetText(self, *args, **kwds): | |
268 | self.SetReadOnly(False) | |
269 | editwindow.EditWindow.SetText(self, *args, **kwds) | |
270 | self.SetReadOnly(True) | |
271 | ||
272 | ||
273 | class Filling(wx.SplitterWindow): | |
274 | """Filling based on wxSplitterWindow.""" | |
275 | ||
276 | name = 'Filling' | |
277 | revision = __revision__ | |
278 | ||
279 | def __init__(self, parent, id=-1, pos=wx.DefaultPosition, | |
280 | size=wx.DefaultSize, style=wx.SP_3D, | |
281 | name='Filling Window', rootObject=None, | |
282 | rootLabel=None, rootIsNamespace=False, static=False): | |
283 | """Create a Filling instance.""" | |
284 | wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name) | |
285 | self.tree = FillingTree(parent=self, rootObject=rootObject, | |
286 | rootLabel=rootLabel, | |
287 | rootIsNamespace=rootIsNamespace, | |
288 | static=static) | |
289 | self.text = FillingText(parent=self, static=static) | |
290 | self.SplitVertically(self.tree, self.text, 130) | |
291 | self.SetMinimumPaneSize(1) | |
292 | # Override the filling so that descriptions go to FillingText. | |
293 | self.tree.setText = self.text.SetText | |
294 | # Display the root item. | |
295 | ## self.tree.SelectItem(self.tree.root) | |
296 | self.tree.display() | |
297 | ||
298 | ||
299 | class FillingFrame(wx.Frame): | |
300 | """Frame containing the namespace tree component.""" | |
301 | ||
302 | name = 'Filling Frame' | |
303 | revision = __revision__ | |
304 | ||
305 | def __init__(self, parent=None, id=-1, title='PyFilling', | |
306 | pos=wx.DefaultPosition, size=(600, 400), | |
307 | style=wx.DEFAULT_FRAME_STYLE, rootObject=None, | |
308 | rootLabel=None, rootIsNamespace=False, static=False): | |
309 | """Create FillingFrame instance.""" | |
310 | wx.Frame.__init__(self, parent, id, title, pos, size, style) | |
311 | intro = 'PyFilling - The Tastiest Namespace Inspector' | |
312 | self.CreateStatusBar() | |
313 | self.SetStatusText(intro) | |
314 | import images | |
315 | self.SetIcon(images.getPyIcon()) | |
316 | self.filling = Filling(parent=self, rootObject=rootObject, | |
317 | rootLabel=rootLabel, | |
318 | rootIsNamespace=rootIsNamespace, | |
319 | static=static) | |
320 | # Override so that status messages go to the status bar. | |
321 | self.filling.tree.setStatusText = self.SetStatusText | |
322 | ||
323 | ||
324 | class App(wx.App): | |
325 | """PyFilling standalone application.""" | |
326 | ||
327 | def OnInit(self): | |
328 | wx.InitAllImageHandlers() | |
329 | self.fillingFrame = FillingFrame() | |
330 | self.fillingFrame.Show(True) | |
331 | self.SetTopWindow(self.fillingFrame) | |
332 | return True |