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