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>"
6 __revision__
= "$Revision$"[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
)