1
"""Filling is the gui tree control through which a user can navigate
2
the local namespace or any object."""
4
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
5
__cvsid__ = "$Id: filling.py,v 1.5.2.3 2006/02/18 21:40:00 RD Exp $"
6
__revision__ = "$Revision: 1.5.2.3 $"[11:-2]
17
from version import VERSION
20
COMMONTYPES = [getattr(types, t) for t in dir(types) \
21
if not t.startswith('_') \
22
and t not in ('ClassType', 'InstanceType', 'ModuleType')]
24
DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
25
'FunctionType', 'GeneratorType', 'InstanceType',
26
'LambdaType', 'MethodType', 'ModuleType',
27
'UnboundMethodType', 'method-wrapper')
29
SIMPLETYPES = [getattr(types, t) for t in dir(types) \
30
if not t.startswith('_') and t not in DOCTYPES]
35
COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x.
36
except AttributeError:
40
class FillingTree(wx.TreeCtrl):
41
"""FillingTree based on TreeCtrl."""
44
revision = __revision__
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,
50
"""Create FillingTree instance."""
51
wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
52
self.rootIsNamespace = rootIsNamespace
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()'
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())
69
dispatcher.connect(receiver=self.push, signal='Interpreter.push')
71
def push(self, command, more):
72
"""Receiver for Interpreter.push signal."""
75
def OnItemExpanding(self, event):
76
"""Add children to the item."""
77
busy = wx.BusyCursor()
78
item = event.GetItem()
79
if self.IsExpanded(item):
81
self.addChildren(item)
82
# self.SelectItem(item)
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)
92
def OnSelChanged(self, event):
93
"""Display information about the item."""
94
busy = wx.BusyCursor()
95
self.item = event.GetItem()
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)
107
def objHasChildren(self, obj):
108
"""Return true if object has children."""
109
if self.objGetChildren(obj):
114
def objGetChildren(self, obj):
115
"""Return dictionary with attributes or contents of object."""
116
busy = wx.BusyCursor()
118
if otype is types.DictType \
119
or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'):
122
if otype is types.ListType or otype is types.TupleType:
123
for n in range(len(obj)):
124
key = '[' + str(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.
132
d[key] = getattr(obj, key)
137
def addChildren(self, item):
138
self.DeleteChildren(item)
139
obj = self.GetPyData(item)
140
children = self.objGetChildren(obj)
143
keys = children.keys()
144
keys.sort(lambda x, y: cmp(str(x).lower(), str(y).lower()))
147
# Show string dictionary items with single quotes, except
148
# for the first level of items, if they represent a
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)):
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))
164
if self.IsExpanded(item):
165
self.addChildren(item)
167
obj = self.GetPyData(item)
168
if wx.Platform == '__WXMSW__':
169
if obj is None: # Windows bug fix.
171
self.SetItemHasChildren(item, self.objHasChildren(obj))
174
text += self.getFullName(item)
175
text += '\n\nType: ' + str(otype)
180
if otype is types.StringType or otype is types.UnicodeType:
182
text += '\n\nValue: ' + value
183
if otype not in SIMPLETYPES:
185
text += '\n\nDocstring:\n\n"""' + \
186
inspect.getdoc(obj).strip() + '"""'
189
if otype is types.InstanceType:
191
text += '\n\nClass Definition:\n\n' + \
192
inspect.getsource(obj.__class__)
197
text += '\n\nSource Code:\n\n' + \
198
inspect.getsource(obj)
203
def getFullName(self, item, partial=''):
204
"""Return a syntactically proper name for item."""
205
name = self.GetItemText(item)
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.
221
if partial[0] == '[':
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)
232
def setText(self, text):
233
"""Display information about the current selection."""
235
# This method will likely be replaced by the enclosing app to
236
# do something more interesting, like write to a text control.
239
def setStatusText(self, text):
240
"""Display status information."""
242
# This method will likely be replaced by the enclosing app to
243
# do something more interesting, like write to a status bar.
247
class FillingText(editwindow.EditWindow):
248
"""FillingText based on StyledTextCtrl."""
250
name = 'Filling Text'
251
revision = __revision__
253
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
254
size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
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)
263
dispatcher.connect(receiver=self.push, signal='Interpreter.push')
265
def push(self, command, more):
266
"""Receiver for Interpreter.push signal."""
269
def SetText(self, *args, **kwds):
270
self.SetReadOnly(False)
271
editwindow.EditWindow.SetText(self, *args, **kwds)
272
self.SetReadOnly(True)
275
class Filling(wx.SplitterWindow):
276
"""Filling based on wxSplitterWindow."""
279
revision = __revision__
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)
288
self.tree = FillingTree(parent=self, rootObject=rootObject,
290
rootIsNamespace=rootIsNamespace,
292
self.text = FillingText(parent=self, static=static)
294
wx.FutureCall(1, self.SplitVertically, self.tree, self.text, 200)
296
self.SetMinimumPaneSize(1)
298
# Override the filling so that descriptions go to FillingText.
299
self.tree.setText = self.text.SetText
301
# Display the root item.
302
self.tree.SelectItem(self.tree.root)
305
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged)
307
def OnChanged(self, event):
308
#this is important: do not evaluate this event=> otherwise, splitterwindow behaves strange
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)
318
self.text.SetZoom(zoom)
320
def SaveSettings(self, config):
321
config.WriteInt('Sash/FillingPos', self.GetSashPosition())
322
config.WriteInt('View/Zoom/Filling', self.text.GetZoom())
326
class FillingFrame(wx.Frame):
327
"""Frame containing the namespace tree component."""
329
name = 'Filling Frame'
330
revision = __revision__
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)
342
self.SetIcon(images.getPyIcon())
343
self.filling = Filling(parent=self, rootObject=rootObject,
345
rootIsNamespace=rootIsNamespace,
347
# Override so that status messages go to the status bar.
348
self.filling.tree.setStatusText = self.SetStatusText
352
"""PyFilling standalone application."""
355
wx.InitAllImageHandlers()
356
self.fillingFrame = FillingFrame()
357
self.fillingFrame.Show(True)
358
self.SetTopWindow(self.fillingFrame)