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
))
162 if self
.IsExpanded(item
):
163 self
.addChildren(item
)
165 obj
= self
.GetPyData(item
)
166 if wx
.Platform
== '__WXMSW__':
167 if obj
is None: # Windows bug fix.
169 self
.SetItemHasChildren(item
, self
.objHasChildren(obj
))
172 text
+= self
.getFullName(item
)
173 text
+= '\n\nType: ' + str(otype
)
178 if otype
is types
.StringType
or otype
is types
.UnicodeType
:
180 text
+= '\n\nValue: ' + value
181 if otype
not in SIMPLETYPES
:
183 text
+= '\n\nDocstring:\n\n"""' + \
184 inspect
.getdoc(obj
).strip() + '"""'
187 if otype
is types
.InstanceType
:
189 text
+= '\n\nClass Definition:\n\n' + \
190 inspect
.getsource(obj
.__class
__)
195 text
+= '\n\nSource Code:\n\n' + \
196 inspect
.getsource(obj
)
201 def getFullName(self
, item
, partial
=''):
202 """Return a syntactically proper name for item."""
203 name
= self
.GetItemText(item
)
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.
219 if partial
[0] == '[':
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
)
230 def setText(self
, text
):
231 """Display information about the current selection."""
233 # This method will likely be replaced by the enclosing app to
234 # do something more interesting, like write to a text control.
237 def setStatusText(self
, text
):
238 """Display status information."""
240 # This method will likely be replaced by the enclosing app to
241 # do something more interesting, like write to a status bar.
245 class FillingText(editwindow
.EditWindow
):
246 """FillingText based on StyledTextCtrl."""
248 name
= 'Filling Text'
249 revision
= __revision__
251 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
,
252 size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
,
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)
261 dispatcher
.connect(receiver
=self
.push
, signal
='Interpreter.push')
263 def push(self
, command
, more
):
264 """Receiver for Interpreter.push signal."""
267 def SetText(self
, *args
, **kwds
):
268 self
.SetReadOnly(False)
269 editwindow
.EditWindow
.SetText(self
, *args
, **kwds
)
270 self
.SetReadOnly(True)
273 class Filling(wx
.SplitterWindow
):
274 """Filling based on wxSplitterWindow."""
277 revision
= __revision__
279 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
,
280 size
=wx
.DefaultSize
, style
=wx
.SP_3D|wx
.SP_LIVE_UPDATE
,
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
)
286 self
.tree
= FillingTree(parent
=self
, rootObject
=rootObject
,
288 rootIsNamespace
=rootIsNamespace
,
290 self
.text
= FillingText(parent
=self
, static
=static
)
292 wx
.FutureCall(1, self
.SplitVertically
, self
.tree
, self
.text
, 200)
294 self
.SetMinimumPaneSize(1)
296 # Override the filling so that descriptions go to FillingText.
297 self
.tree
.setText
= self
.text
.SetText
299 # Display the root item.
300 self
.tree
.SelectItem(self
.tree
.root
)
303 self
.Bind(wx
.EVT_SPLITTER_SASH_POS_CHANGED
, self
.OnChanged
)
305 def OnChanged(self
, event
):
306 #this is important: do not evaluate this event=> otherwise, splitterwindow behaves strange
311 def LoadSettings(self
, config
):
312 pos
= config
.ReadInt('Sash/FillingPos', 200)
313 wx
.FutureCall(250, self
.SetSashPosition
, pos
)
314 zoom
= config
.ReadInt('View/Zoom/Filling', -99)
316 self
.text
.SetZoom(zoom
)
318 def SaveSettings(self
, config
):
319 config
.WriteInt('Sash/FillingPos', self
.GetSashPosition())
320 config
.WriteInt('View/Zoom/Filling', self
.text
.GetZoom())
324 class FillingFrame(wx
.Frame
):
325 """Frame containing the namespace tree component."""
327 name
= 'Filling Frame'
328 revision
= __revision__
330 def __init__(self
, parent
=None, id=-1, title
='PyFilling',
331 pos
=wx
.DefaultPosition
, size
=(600, 400),
332 style
=wx
.DEFAULT_FRAME_STYLE
, rootObject
=None,
333 rootLabel
=None, rootIsNamespace
=False, static
=False):
334 """Create FillingFrame instance."""
335 wx
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
336 intro
= 'PyFilling - The Tastiest Namespace Inspector'
337 self
.CreateStatusBar()
338 self
.SetStatusText(intro
)
340 self
.SetIcon(images
.getPyIcon())
341 self
.filling
= Filling(parent
=self
, rootObject
=rootObject
,
343 rootIsNamespace
=rootIsNamespace
,
345 # Override so that status messages go to the status bar.
346 self
.filling
.tree
.setStatusText
= self
.SetStatusText
350 """PyFilling standalone application."""
353 wx
.InitAllImageHandlers()
354 self
.fillingFrame
= FillingFrame()
355 self
.fillingFrame
.Show(True)
356 self
.SetTopWindow(self
.fillingFrame
)