]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/py/filling.py
Prevent using a bad tree item.
[wxWidgets.git] / wxPython / wx / py / filling.py
1 """Filling is the gui tree control through which a user can navigate
2 the local namespace or any object."""
3
4 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
5 __cvsid__ = "$Id$"
6 __revision__ = "$Revision$"[11:-2]
7
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
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 self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, id=self.GetId())
65 self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=self.GetId())
66 self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=self.GetId())
67 self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated, id=self.GetId())
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 not item:
163 return
164 if self.IsExpanded(item):
165 self.addChildren(item)
166 self.setText('')
167 obj = self.GetPyData(item)
168 if wx.Platform == '__WXMSW__':
169 if obj is None: # Windows bug fix.
170 return
171 self.SetItemHasChildren(item, self.objHasChildren(obj))
172 otype = type(obj)
173 text = ''
174 text += self.getFullName(item)
175 text += '\n\nType: ' + str(otype)
176 try:
177 value = str(obj)
178 except:
179 value = ''
180 if otype is types.StringType or otype is types.UnicodeType:
181 value = repr(obj)
182 text += '\n\nValue: ' + value
183 if otype not in SIMPLETYPES:
184 try:
185 text += '\n\nDocstring:\n\n"""' + \
186 inspect.getdoc(obj).strip() + '"""'
187 except:
188 pass
189 if otype is types.InstanceType:
190 try:
191 text += '\n\nClass Definition:\n\n' + \
192 inspect.getsource(obj.__class__)
193 except:
194 pass
195 else:
196 try:
197 text += '\n\nSource Code:\n\n' + \
198 inspect.getsource(obj)
199 except:
200 pass
201 self.setText(text)
202
203 def getFullName(self, item, partial=''):
204 """Return a syntactically proper name for item."""
205 name = self.GetItemText(item)
206 parent = None
207 obj = None
208 if item != self.root:
209 parent = self.GetItemParent(item)
210 obj = self.GetPyData(parent)
211 # Apply dictionary syntax to dictionary items, except the root
212 # and first level children of a namepace.
213 if (type(obj) is types.DictType \
214 or str(type(obj))[17:23] == 'BTrees' \
215 and hasattr(obj, 'keys')) \
216 and ((item != self.root and parent != self.root) \
217 or (parent == self.root and not self.rootIsNamespace)):
218 name = '[' + name + ']'
219 # Apply dot syntax to multipart names.
220 if partial:
221 if partial[0] == '[':
222 name += partial
223 else:
224 name += '.' + partial
225 # Repeat for everything but the root item
226 # and first level children of a namespace.
227 if (item != self.root and parent != self.root) \
228 or (parent == self.root and not self.rootIsNamespace):
229 name = self.getFullName(parent, partial=name)
230 return name
231
232 def setText(self, text):
233 """Display information about the current selection."""
234
235 # This method will likely be replaced by the enclosing app to
236 # do something more interesting, like write to a text control.
237 print text
238
239 def setStatusText(self, text):
240 """Display status information."""
241
242 # This method will likely be replaced by the enclosing app to
243 # do something more interesting, like write to a status bar.
244 print text
245
246
247 class FillingText(editwindow.EditWindow):
248 """FillingText based on StyledTextCtrl."""
249
250 name = 'Filling Text'
251 revision = __revision__
252
253 def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
254 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
255 static=False):
256 """Create FillingText instance."""
257 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
258 # Configure various defaults and user preferences.
259 self.SetReadOnly(True)
260 self.SetWrapMode(True)
261 self.SetMarginWidth(1, 0)
262 if not static:
263 dispatcher.connect(receiver=self.push, signal='Interpreter.push')
264
265 def push(self, command, more):
266 """Receiver for Interpreter.push signal."""
267 self.Refresh()
268
269 def SetText(self, *args, **kwds):
270 self.SetReadOnly(False)
271 editwindow.EditWindow.SetText(self, *args, **kwds)
272 self.SetReadOnly(True)
273
274
275 class Filling(wx.SplitterWindow):
276 """Filling based on wxSplitterWindow."""
277
278 name = 'Filling'
279 revision = __revision__
280
281 def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
282 size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE,
283 name='Filling Window', rootObject=None,
284 rootLabel=None, rootIsNamespace=False, static=False):
285 """Create a Filling instance."""
286 wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name)
287
288 self.tree = FillingTree(parent=self, rootObject=rootObject,
289 rootLabel=rootLabel,
290 rootIsNamespace=rootIsNamespace,
291 static=static)
292 self.text = FillingText(parent=self, static=static)
293
294 wx.FutureCall(1, self.SplitVertically, self.tree, self.text, 200)
295
296 self.SetMinimumPaneSize(1)
297
298 # Override the filling so that descriptions go to FillingText.
299 self.tree.setText = self.text.SetText
300
301 # Display the root item.
302 self.tree.SelectItem(self.tree.root)
303 self.tree.display()
304
305 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged)
306
307 def OnChanged(self, event):
308 #this is important: do not evaluate this event=> otherwise, splitterwindow behaves strange
309 #event.Skip()
310 pass
311
312
313 def LoadSettings(self, config):
314 pos = config.ReadInt('Sash/FillingPos', 200)
315 wx.FutureCall(250, self.SetSashPosition, pos)
316 zoom = config.ReadInt('View/Zoom/Filling', -99)
317 if zoom != -99:
318 self.text.SetZoom(zoom)
319
320 def SaveSettings(self, config):
321 config.WriteInt('Sash/FillingPos', self.GetSashPosition())
322 config.WriteInt('View/Zoom/Filling', self.text.GetZoom())
323
324
325
326 class FillingFrame(wx.Frame):
327 """Frame containing the namespace tree component."""
328
329 name = 'Filling Frame'
330 revision = __revision__
331
332 def __init__(self, parent=None, id=-1, title='PyFilling',
333 pos=wx.DefaultPosition, size=(600, 400),
334 style=wx.DEFAULT_FRAME_STYLE, rootObject=None,
335 rootLabel=None, rootIsNamespace=False, static=False):
336 """Create FillingFrame instance."""
337 wx.Frame.__init__(self, parent, id, title, pos, size, style)
338 intro = 'PyFilling - The Tastiest Namespace Inspector'
339 self.CreateStatusBar()
340 self.SetStatusText(intro)
341 import images
342 self.SetIcon(images.getPyIcon())
343 self.filling = Filling(parent=self, rootObject=rootObject,
344 rootLabel=rootLabel,
345 rootIsNamespace=rootIsNamespace,
346 static=static)
347 # Override so that status messages go to the status bar.
348 self.filling.tree.setStatusText = self.SetStatusText
349
350
351 class App(wx.App):
352 """PyFilling standalone application."""
353
354 def OnInit(self):
355 wx.InitAllImageHandlers()
356 self.fillingFrame = FillingFrame()
357 self.fillingFrame.Show(True)
358 self.SetTopWindow(self.fillingFrame)
359 return True