2003-11-12 16:34:20 -05:00
|
|
|
"""Filling is the gui tree control through which a user can navigate
|
|
|
|
the local namespace or any object."""
|
2003-07-02 19:13:10 -04:00
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
|
|
|
__cvsid__ = "$Id$"
|
|
|
|
__revision__ = "$Revision$"[11:-2]
|
2003-07-02 19:13:10 -04:00
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
import wx
|
|
|
|
|
|
|
|
import dispatcher
|
|
|
|
import editwindow
|
|
|
|
import inspect
|
|
|
|
import introspect
|
|
|
|
import keyword
|
|
|
|
import sys
|
|
|
|
import types
|
|
|
|
from version import VERSION
|
|
|
|
|
|
|
|
|
|
|
|
COMMONTYPES = [getattr(types, t) for t in dir(types) \
|
|
|
|
if not t.startswith('_') \
|
|
|
|
and t not in ('ClassType', 'InstanceType', 'ModuleType')]
|
|
|
|
|
|
|
|
DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
|
|
|
|
'FunctionType', 'GeneratorType', 'InstanceType',
|
|
|
|
'LambdaType', 'MethodType', 'ModuleType',
|
|
|
|
'UnboundMethodType', 'method-wrapper')
|
|
|
|
|
|
|
|
SIMPLETYPES = [getattr(types, t) for t in dir(types) \
|
|
|
|
if not t.startswith('_') and t not in DOCTYPES]
|
|
|
|
|
|
|
|
del t
|
|
|
|
|
|
|
|
try:
|
|
|
|
COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x.
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class FillingTree(wx.TreeCtrl):
|
|
|
|
"""FillingTree based on TreeCtrl."""
|
|
|
|
|
|
|
|
name = 'Filling Tree'
|
|
|
|
revision = __revision__
|
|
|
|
|
|
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
|
|
|
|
size=wx.DefaultSize, style=wx.TR_DEFAULT_STYLE,
|
|
|
|
rootObject=None, rootLabel=None, rootIsNamespace=False,
|
|
|
|
static=False):
|
|
|
|
"""Create FillingTree instance."""
|
|
|
|
wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
|
|
|
|
self.rootIsNamespace = rootIsNamespace
|
|
|
|
import __main__
|
|
|
|
if rootObject is None:
|
|
|
|
rootObject = __main__.__dict__
|
|
|
|
self.rootIsNamespace = True
|
|
|
|
if rootObject is __main__.__dict__ and rootLabel is None:
|
|
|
|
rootLabel = 'locals()'
|
|
|
|
if not rootLabel:
|
|
|
|
rootLabel = 'Ingredients'
|
|
|
|
rootData = wx.TreeItemData(rootObject)
|
|
|
|
self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData)
|
|
|
|
self.SetItemHasChildren(self.root, self.objHasChildren(rootObject))
|
2005-12-30 18:02:03 -05:00
|
|
|
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, id=self.GetId())
|
|
|
|
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=self.GetId())
|
|
|
|
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=self.GetId())
|
|
|
|
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated, id=self.GetId())
|
2003-11-12 16:34:20 -05:00
|
|
|
if not static:
|
|
|
|
dispatcher.connect(receiver=self.push, signal='Interpreter.push')
|
|
|
|
|
|
|
|
def push(self, command, more):
|
|
|
|
"""Receiver for Interpreter.push signal."""
|
|
|
|
self.display()
|
|
|
|
|
|
|
|
def OnItemExpanding(self, event):
|
|
|
|
"""Add children to the item."""
|
|
|
|
busy = wx.BusyCursor()
|
|
|
|
item = event.GetItem()
|
|
|
|
if self.IsExpanded(item):
|
|
|
|
return
|
|
|
|
self.addChildren(item)
|
|
|
|
# self.SelectItem(item)
|
|
|
|
|
|
|
|
def OnItemCollapsed(self, event):
|
|
|
|
"""Remove all children from the item."""
|
|
|
|
busy = wx.BusyCursor()
|
|
|
|
item = event.GetItem()
|
|
|
|
# self.CollapseAndReset(item)
|
|
|
|
# self.DeleteChildren(item)
|
|
|
|
# self.SelectItem(item)
|
|
|
|
|
|
|
|
def OnSelChanged(self, event):
|
|
|
|
"""Display information about the item."""
|
|
|
|
busy = wx.BusyCursor()
|
|
|
|
self.item = event.GetItem()
|
|
|
|
self.display()
|
|
|
|
|
|
|
|
def OnItemActivated(self, event):
|
|
|
|
"""Launch a DirFrame."""
|
|
|
|
item = event.GetItem()
|
|
|
|
text = self.getFullName(item)
|
|
|
|
obj = self.GetPyData(item)
|
|
|
|
frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj,
|
|
|
|
rootLabel=text, rootIsNamespace=False)
|
|
|
|
frame.Show()
|
|
|
|
|
|
|
|
def objHasChildren(self, obj):
|
|
|
|
"""Return true if object has children."""
|
|
|
|
if self.objGetChildren(obj):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def objGetChildren(self, obj):
|
|
|
|
"""Return dictionary with attributes or contents of object."""
|
|
|
|
busy = wx.BusyCursor()
|
|
|
|
otype = type(obj)
|
|
|
|
if otype is types.DictType \
|
|
|
|
or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'):
|
|
|
|
return obj
|
|
|
|
d = {}
|
|
|
|
if otype is types.ListType or otype is types.TupleType:
|
|
|
|
for n in range(len(obj)):
|
|
|
|
key = '[' + str(n) + ']'
|
|
|
|
d[key] = obj[n]
|
|
|
|
if otype not in COMMONTYPES:
|
|
|
|
for key in introspect.getAttributeNames(obj):
|
|
|
|
# Believe it or not, some attributes can disappear,
|
|
|
|
# such as the exc_traceback attribute of the sys
|
|
|
|
# module. So this is nested in a try block.
|
|
|
|
try:
|
|
|
|
d[key] = getattr(obj, key)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return d
|
|
|
|
|
|
|
|
def addChildren(self, item):
|
|
|
|
self.DeleteChildren(item)
|
|
|
|
obj = self.GetPyData(item)
|
|
|
|
children = self.objGetChildren(obj)
|
|
|
|
if not children:
|
|
|
|
return
|
|
|
|
keys = children.keys()
|
|
|
|
keys.sort(lambda x, y: cmp(str(x).lower(), str(y).lower()))
|
|
|
|
for key in keys:
|
|
|
|
itemtext = str(key)
|
|
|
|
# Show string dictionary items with single quotes, except
|
|
|
|
# for the first level of items, if they represent a
|
|
|
|
# namespace.
|
|
|
|
if type(obj) is types.DictType \
|
|
|
|
and type(key) is types.StringType \
|
|
|
|
and (item != self.root \
|
|
|
|
or (item == self.root and not self.rootIsNamespace)):
|
|
|
|
itemtext = repr(key)
|
|
|
|
child = children[key]
|
|
|
|
data = wx.TreeItemData(child)
|
|
|
|
branch = self.AppendItem(parent=item, text=itemtext, data=data)
|
|
|
|
self.SetItemHasChildren(branch, self.objHasChildren(child))
|
|
|
|
|
|
|
|
def display(self):
|
|
|
|
item = self.item
|
2006-02-18 16:40:57 -05:00
|
|
|
if not item:
|
|
|
|
return
|
2003-11-12 16:34:20 -05:00
|
|
|
if self.IsExpanded(item):
|
|
|
|
self.addChildren(item)
|
|
|
|
self.setText('')
|
|
|
|
obj = self.GetPyData(item)
|
|
|
|
if wx.Platform == '__WXMSW__':
|
|
|
|
if obj is None: # Windows bug fix.
|
|
|
|
return
|
|
|
|
self.SetItemHasChildren(item, self.objHasChildren(obj))
|
|
|
|
otype = type(obj)
|
|
|
|
text = ''
|
|
|
|
text += self.getFullName(item)
|
|
|
|
text += '\n\nType: ' + str(otype)
|
|
|
|
try:
|
|
|
|
value = str(obj)
|
|
|
|
except:
|
|
|
|
value = ''
|
|
|
|
if otype is types.StringType or otype is types.UnicodeType:
|
|
|
|
value = repr(obj)
|
|
|
|
text += '\n\nValue: ' + value
|
|
|
|
if otype not in SIMPLETYPES:
|
|
|
|
try:
|
|
|
|
text += '\n\nDocstring:\n\n"""' + \
|
|
|
|
inspect.getdoc(obj).strip() + '"""'
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if otype is types.InstanceType:
|
|
|
|
try:
|
|
|
|
text += '\n\nClass Definition:\n\n' + \
|
|
|
|
inspect.getsource(obj.__class__)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
text += '\n\nSource Code:\n\n' + \
|
|
|
|
inspect.getsource(obj)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
self.setText(text)
|
|
|
|
|
|
|
|
def getFullName(self, item, partial=''):
|
|
|
|
"""Return a syntactically proper name for item."""
|
|
|
|
name = self.GetItemText(item)
|
|
|
|
parent = None
|
|
|
|
obj = None
|
|
|
|
if item != self.root:
|
|
|
|
parent = self.GetItemParent(item)
|
|
|
|
obj = self.GetPyData(parent)
|
|
|
|
# Apply dictionary syntax to dictionary items, except the root
|
|
|
|
# and first level children of a namepace.
|
|
|
|
if (type(obj) is types.DictType \
|
|
|
|
or str(type(obj))[17:23] == 'BTrees' \
|
|
|
|
and hasattr(obj, 'keys')) \
|
|
|
|
and ((item != self.root and parent != self.root) \
|
|
|
|
or (parent == self.root and not self.rootIsNamespace)):
|
|
|
|
name = '[' + name + ']'
|
|
|
|
# Apply dot syntax to multipart names.
|
|
|
|
if partial:
|
|
|
|
if partial[0] == '[':
|
|
|
|
name += partial
|
|
|
|
else:
|
|
|
|
name += '.' + partial
|
|
|
|
# Repeat for everything but the root item
|
|
|
|
# and first level children of a namespace.
|
|
|
|
if (item != self.root and parent != self.root) \
|
|
|
|
or (parent == self.root and not self.rootIsNamespace):
|
|
|
|
name = self.getFullName(parent, partial=name)
|
|
|
|
return name
|
|
|
|
|
|
|
|
def setText(self, text):
|
|
|
|
"""Display information about the current selection."""
|
|
|
|
|
|
|
|
# This method will likely be replaced by the enclosing app to
|
|
|
|
# do something more interesting, like write to a text control.
|
|
|
|
print text
|
|
|
|
|
|
|
|
def setStatusText(self, text):
|
|
|
|
"""Display status information."""
|
|
|
|
|
|
|
|
# This method will likely be replaced by the enclosing app to
|
|
|
|
# do something more interesting, like write to a status bar.
|
|
|
|
print text
|
|
|
|
|
|
|
|
|
|
|
|
class FillingText(editwindow.EditWindow):
|
|
|
|
"""FillingText based on StyledTextCtrl."""
|
|
|
|
|
|
|
|
name = 'Filling Text'
|
|
|
|
revision = __revision__
|
|
|
|
|
|
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
|
|
|
|
size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
|
|
|
|
static=False):
|
|
|
|
"""Create FillingText instance."""
|
|
|
|
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
|
|
|
|
# Configure various defaults and user preferences.
|
|
|
|
self.SetReadOnly(True)
|
|
|
|
self.SetWrapMode(True)
|
|
|
|
self.SetMarginWidth(1, 0)
|
|
|
|
if not static:
|
|
|
|
dispatcher.connect(receiver=self.push, signal='Interpreter.push')
|
|
|
|
|
|
|
|
def push(self, command, more):
|
|
|
|
"""Receiver for Interpreter.push signal."""
|
|
|
|
self.Refresh()
|
|
|
|
|
|
|
|
def SetText(self, *args, **kwds):
|
|
|
|
self.SetReadOnly(False)
|
|
|
|
editwindow.EditWindow.SetText(self, *args, **kwds)
|
|
|
|
self.SetReadOnly(True)
|
|
|
|
|
|
|
|
|
|
|
|
class Filling(wx.SplitterWindow):
|
|
|
|
"""Filling based on wxSplitterWindow."""
|
|
|
|
|
|
|
|
name = 'Filling'
|
|
|
|
revision = __revision__
|
|
|
|
|
|
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
|
2005-12-30 18:02:03 -05:00
|
|
|
size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE,
|
2003-11-12 16:34:20 -05:00
|
|
|
name='Filling Window', rootObject=None,
|
|
|
|
rootLabel=None, rootIsNamespace=False, static=False):
|
|
|
|
"""Create a Filling instance."""
|
|
|
|
wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name)
|
2005-12-30 18:02:03 -05:00
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
self.tree = FillingTree(parent=self, rootObject=rootObject,
|
|
|
|
rootLabel=rootLabel,
|
|
|
|
rootIsNamespace=rootIsNamespace,
|
|
|
|
static=static)
|
|
|
|
self.text = FillingText(parent=self, static=static)
|
2005-12-30 18:02:03 -05:00
|
|
|
|
|
|
|
wx.FutureCall(1, self.SplitVertically, self.tree, self.text, 200)
|
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
self.SetMinimumPaneSize(1)
|
2005-12-30 18:02:03 -05:00
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
# Override the filling so that descriptions go to FillingText.
|
|
|
|
self.tree.setText = self.text.SetText
|
2005-12-30 18:02:03 -05:00
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
# Display the root item.
|
2005-12-30 18:02:03 -05:00
|
|
|
self.tree.SelectItem(self.tree.root)
|
2003-11-12 16:34:20 -05:00
|
|
|
self.tree.display()
|
|
|
|
|
2005-12-30 18:02:03 -05:00
|
|
|
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged)
|
|
|
|
|
|
|
|
def OnChanged(self, event):
|
|
|
|
#this is important: do not evaluate this event=> otherwise, splitterwindow behaves strange
|
|
|
|
#event.Skip()
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def LoadSettings(self, config):
|
|
|
|
pos = config.ReadInt('Sash/FillingPos', 200)
|
|
|
|
wx.FutureCall(250, self.SetSashPosition, pos)
|
|
|
|
zoom = config.ReadInt('View/Zoom/Filling', -99)
|
|
|
|
if zoom != -99:
|
|
|
|
self.text.SetZoom(zoom)
|
|
|
|
|
|
|
|
def SaveSettings(self, config):
|
|
|
|
config.WriteInt('Sash/FillingPos', self.GetSashPosition())
|
|
|
|
config.WriteInt('View/Zoom/Filling', self.text.GetZoom())
|
|
|
|
|
|
|
|
|
2003-11-12 16:34:20 -05:00
|
|
|
|
|
|
|
class FillingFrame(wx.Frame):
|
|
|
|
"""Frame containing the namespace tree component."""
|
|
|
|
|
|
|
|
name = 'Filling Frame'
|
|
|
|
revision = __revision__
|
|
|
|
|
|
|
|
def __init__(self, parent=None, id=-1, title='PyFilling',
|
|
|
|
pos=wx.DefaultPosition, size=(600, 400),
|
|
|
|
style=wx.DEFAULT_FRAME_STYLE, rootObject=None,
|
|
|
|
rootLabel=None, rootIsNamespace=False, static=False):
|
|
|
|
"""Create FillingFrame instance."""
|
|
|
|
wx.Frame.__init__(self, parent, id, title, pos, size, style)
|
|
|
|
intro = 'PyFilling - The Tastiest Namespace Inspector'
|
|
|
|
self.CreateStatusBar()
|
|
|
|
self.SetStatusText(intro)
|
|
|
|
import images
|
|
|
|
self.SetIcon(images.getPyIcon())
|
|
|
|
self.filling = Filling(parent=self, rootObject=rootObject,
|
|
|
|
rootLabel=rootLabel,
|
|
|
|
rootIsNamespace=rootIsNamespace,
|
|
|
|
static=static)
|
|
|
|
# Override so that status messages go to the status bar.
|
|
|
|
self.filling.tree.setStatusText = self.SetStatusText
|
|
|
|
|
|
|
|
|
|
|
|
class App(wx.App):
|
|
|
|
"""PyFilling standalone application."""
|
|
|
|
|
|
|
|
def OnInit(self):
|
|
|
|
wx.InitAllImageHandlers()
|
|
|
|
self.fillingFrame = FillingFrame()
|
|
|
|
self.fillingFrame.Show(True)
|
|
|
|
self.SetTopWindow(self.fillingFrame)
|
|
|
|
return True
|