Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/inspection.py
Master Jumblespeed d882959bfa switch to remote
2015-05-18 22:11:33 -04:00

1248 lines
50 KiB
Python
Executable file

#----------------------------------------------------------------------------
# Name: wx.lib.inspection
# Purpose: A widget inspection tool that allows easy introspection of
# all the live widgets and sizers in an application.
#
# Author: Robin Dunn
#
# Created: 26-Jan-2007
# Copyright: (c) 2007 by Total Control Software
# Licence: wxWindows license
#
# Tags: py3-port, phoenix-port, documented
#----------------------------------------------------------------------------
# NOTE: This class was originally based on ideas sent to the
# wxPython-users mail list by Dan Eloff. See also
# wx.lib.mixins.inspect for a class that can be mixed-in with wx.App
# to provide Hot-Key access to the inspection tool.
"""
This modules provides the :class:`InspectionTool` and everything else needed to
provide the Widget Inspection Tool (WIT).
"""
import wx
import wx.py
import wx.stc
#import wx.aui as aui
import wx.lib.agw.aui as aui
import wx.lib.six as six
import wx.lib.utils as utils
import sys
import inspect
#----------------------------------------------------------------------------
class InspectionTool:
"""
The :class:`InspectionTool` is a singleton that manages creating and
showing an :class:`InspectionFrame`.
"""
# Note: This is the Borg design pattern which ensures that all
# instances of this class are actually using the same set of
# instance data. See
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
if not hasattr(self, 'initialized'):
self.initialized = False
def Init(self, pos=wx.DefaultPosition, size=wx.Size(850,700),
config=None, locals=None, app=None):
"""
Init is used to set some parameters that will be used later
when the inspection tool is shown. Suitable defaults will be
used for all of these parameters if they are not provided.
:param `pos`: The default position to show the frame at
:param `size`: The default size of the frame
:param `config`: A :class:`Config` object to be used to store layout
and other info to when the inspection frame is closed.
This info will be restored the next time the inspection
frame is used.
:param `locals`: A dictionary of names to be added to the PyCrust
namespace.
:param `app`: A reference to the :class:`App` object.
"""
self._frame = None
self._pos = pos
self._size = size
self._config = config
self._locals = locals
self._app = app
if not self._app:
self._app = wx.GetApp()
self.initialized = True
def Show(self, selectObj=None, refreshTree=False):
"""
Creates the inspection frame if it hasn't been already, and
raises it if neccessary.
:param `selectObj`: Pass a widget or sizer to have that object be
preselected in widget tree.
:param boolean `refreshTree`: rebuild the widget tree, default False
"""
if not self.initialized:
self.Init()
parent = self._app.GetTopWindow()
if not selectObj:
selectObj = parent
if not self._frame:
self._frame = InspectionFrame( parent=parent,
pos=self._pos,
size=self._size,
config=self._config,
locals=self._locals,
app=self._app)
if selectObj:
self._frame.SetObj(selectObj)
if refreshTree:
self._frame.RefreshTree()
self._frame.Show()
if self._frame.IsIconized():
self._frame.Iconize(False)
self._frame.Raise()
#----------------------------------------------------------------------------
class InspectionFrame(wx.Frame):
"""
This class is the frame that holds the wxPython inspection tools.
The toolbar and AUI splitters/floating panes are also managed
here. The contents of the tool windows are handled by other
classes.
"""
def __init__(self, wnd=None, locals=None, config=None,
app=None, title="wxPython Widget Inspection Tool",
*args, **kw):
kw['title'] = title
wx.Frame.__init__(self, *args, **kw)
self.SetExtraStyle(wx.WS_EX_BLOCK_EVENTS)
self.includeSizers = False
self.started = False
self.SetIcon(Icon.GetIcon())
self.MakeToolBar()
panel = wx.Panel(self, size=self.GetClientSize())
# tell FrameManager to manage this frame
self.mgr = aui.AuiManager(panel,
aui.AUI_MGR_DEFAULT
| aui.AUI_MGR_TRANSPARENT_DRAG
| aui.AUI_MGR_ALLOW_ACTIVE_PANE)
# make the child tools
self.tree = InspectionTree(panel, size=(100,300))
self.info = InspectionInfoPanel(panel,
style=wx.NO_BORDER,
)
if not locals:
locals = {}
myIntroText = (
"Python %s on %s, wxPython %s\n"
"NOTE: The 'obj' variable refers to the object selected in the tree."
% (sys.version.split()[0], sys.platform, wx.version()))
self.crust = wx.py.crust.Crust(panel, locals=locals,
intro=myIntroText,
showInterpIntro=False,
style=wx.NO_BORDER,
)
self.locals = self.crust.shell.interp.locals
self.crust.shell.interp.introText = ''
self.locals['obj'] = self.obj = wnd
self.locals['app'] = app
self.locals['wx'] = wx
wx.CallAfter(self._postStartup)
# put the chlid tools in AUI panes
self.mgr.AddPane(self.info,
aui.AuiPaneInfo().Name("info").Caption("Object Info").
CenterPane().CaptionVisible(True).
CloseButton(False).MaximizeButton(True)
)
self.mgr.AddPane(self.tree,
aui.AuiPaneInfo().Name("tree").Caption("Widget Tree").
CaptionVisible(True).Left().Dockable(True).Floatable(True).
BestSize((280,200)).CloseButton(False).MaximizeButton(True)
)
self.mgr.AddPane(self.crust,
aui.AuiPaneInfo().Name("crust").Caption("PyCrust").
CaptionVisible(True).Bottom().Dockable(True).Floatable(True).
BestSize((400,200)).CloseButton(False).MaximizeButton(True)
)
self.mgr.Update()
if config is None:
config = wx.Config('wxpyinspector')
self.config = config
self.Bind(wx.EVT_CLOSE, self.OnClose)
if self.Parent:
tlw = self.Parent.GetTopLevelParent()
tlw.Bind(wx.EVT_CLOSE, self.OnClose)
self.LoadSettings(self.config)
self.crust.shell.lineNumbers = False
self.crust.shell.setDisplayLineNumbers(False)
self.crust.shell.SetMarginWidth(1, 0)
def MakeToolBar(self):
tbar = self.CreateToolBar(wx.TB_HORIZONTAL | wx.TB_FLAT | wx.TB_TEXT | wx.NO_BORDER )
tbar.SetToolBitmapSize((24,24))
refreshBmp = Refresh.GetBitmap()
findWidgetBmp = Find.GetBitmap()
showSizersBmp = ShowSizers.GetBitmap()
expandTreeBmp = ExpandTree.GetBitmap()
collapseTreeBmp = CollapseTree.GetBitmap()
highlightItemBmp = HighlightItem.GetBitmap()
evtWatcherBmp = EvtWatcher.GetBitmap()
toggleFillingBmp = ShowFilling.GetBitmap()
refreshTool = tbar.AddTool(-1, 'Refresh', refreshBmp,
shortHelp = 'Refresh widget tree (F1)')
findWidgetTool = tbar.AddTool(-1, 'Find', findWidgetBmp,
shortHelp='Find new target widget. (F2) Click here and\nthen on another widget in the app.')
showSizersTool = tbar.AddTool(-1, 'Sizers', showSizersBmp,
shortHelp='Include sizers in widget tree (F3)',
kind=wx.ITEM_CHECK)
expandTreeTool = tbar.AddTool(-1, 'Expand', expandTreeBmp,
shortHelp='Expand all tree items (F4)')
collapseTreeTool = tbar.AddTool(-1, 'Collapse', collapseTreeBmp,
shortHelp='Collapse all tree items (F5)')
highlightItemTool = tbar.AddTool(-1, 'Highlight', highlightItemBmp,
shortHelp='Attempt to highlight live item (F6)')
evtWatcherTool = tbar.AddTool(-1, 'Events', evtWatcherBmp,
shortHelp='Watch the events of the selected item (F7)')
toggleFillingTool = tbar.AddTool(-1, 'Filling', toggleFillingBmp,
shortHelp='Show PyCrust \'filling\' (F8)',
kind=wx.ITEM_CHECK)
tbar.Realize()
self.Bind(wx.EVT_TOOL, self.OnRefreshTree, refreshTool)
self.Bind(wx.EVT_TOOL, self.OnFindWidget, findWidgetTool)
self.Bind(wx.EVT_TOOL, self.OnShowSizers, showSizersTool)
self.Bind(wx.EVT_TOOL, self.OnExpandTree, expandTreeTool)
self.Bind(wx.EVT_TOOL, self.OnCollapseTree, collapseTreeTool)
self.Bind(wx.EVT_TOOL, self.OnHighlightItem, highlightItemTool)
self.Bind(wx.EVT_TOOL, self.OnWatchEvents, evtWatcherTool)
self.Bind(wx.EVT_TOOL, self.OnToggleFilling, toggleFillingTool)
self.Bind(wx.EVT_UPDATE_UI, self.OnShowSizersUI, showSizersTool)
self.Bind(wx.EVT_UPDATE_UI, self.OnWatchEventsUI, evtWatcherTool)
self.Bind(wx.EVT_UPDATE_UI, self.OnToggleFillingUI, toggleFillingTool)
tbl = wx.AcceleratorTable(
[(wx.ACCEL_NORMAL, wx.WXK_F1, refreshTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F2, findWidgetTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F3, showSizersTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F4, expandTreeTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F5, collapseTreeTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F6, highlightItemTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F7, evtWatcherTool.GetId()),
(wx.ACCEL_NORMAL, wx.WXK_F8, toggleFillingTool.GetId()),
])
self.SetAcceleratorTable(tbl)
def _postStartup(self):
if self.crust.ToolsShown():
self.crust.ToggleTools()
self.UpdateInfo()
self.started = True
def OnClose(self, evt):
evt.Skip()
if not self:
return
self.SaveSettings(self.config)
if hasattr(self, 'mgr'):
self.mgr.UnInit()
del self.mgr
if self.Parent:
tlw = self.Parent.GetTopLevelParent()
tlw.Unbind(wx.EVT_CLOSE, handler=self.OnClose)
def UpdateInfo(self):
self.info.UpdateInfo(self.obj)
def SetObj(self, obj):
if self.obj is obj:
return
self.locals['obj'] = self.obj = obj
self.UpdateInfo()
if not self.tree.built:
self.tree.BuildTree(obj, includeSizers=self.includeSizers)
else:
self.tree.SelectObj(obj)
def HighlightCurrentItem(self):
"""
Draw a highlight rectangle around the item represented by the
current tree selection.
"""
if not hasattr(self, 'highlighter'):
self.highlighter = _InspectionHighlighter()
self.highlighter.HighlightCurrentItem(self.tree)
def RefreshTree(self):
self.tree.BuildTree(self.obj, includeSizers=self.includeSizers)
def OnRefreshTree(self, evt):
self.RefreshTree()
self.UpdateInfo()
def OnFindWidget(self, evt):
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
self.CaptureMouse()
self.finding = wx.BusyInfo("Click on any widget in the app...")
def OnCaptureLost(self, evt):
self.Unbind(wx.EVT_LEFT_DOWN)
self.Unbind(wx.EVT_MOUSE_CAPTURE_LOST)
del self.finding
def OnLeftDown(self, evt):
self.ReleaseMouse()
wnd, pt = wx.FindWindowAtPointer()
if wnd is not None:
self.SetObj(wnd)
else:
wx.Bell()
self.OnCaptureLost(evt)
def OnShowSizers(self, evt):
self.includeSizers = not self.includeSizers
self.RefreshTree()
def OnExpandTree(self, evt):
current = self.tree.GetSelection()
self.tree.ExpandAll()
self.tree.EnsureVisible(current)
def OnCollapseTree(self, evt):
current = self.tree.GetSelection()
self.tree.CollapseAll()
self.tree.EnsureVisible(current)
self.tree.SelectItem(current)
def OnHighlightItem(self, evt):
self.HighlightCurrentItem()
def OnWatchEvents(self, evt):
item = self.tree.GetSelection()
obj = self.tree.GetItemData(item)
if isinstance(obj, wx.Window):
import wx.lib.eventwatcher as ew
watcher = ew.EventWatcher(self)
watcher.watch(obj)
watcher.Show()
def OnWatchEventsUI(self, evt):
item = self.tree.GetSelection()
if item:
obj = self.tree.GetItemData(item)
evt.Enable(isinstance(obj, wx.Window))
def OnToggleFilling(self, evt):
self.crust.ToggleTools()
def OnShowSizersUI(self, evt):
evt.Check(self.includeSizers)
def OnToggleFillingUI(self, evt):
if self.started:
evt.Check(self.crust.ToolsShown())
def LoadSettings(self, config):
self.crust.LoadSettings(config)
self.info.LoadSettings(config)
pos = wx.Point(config.ReadInt('Window/PosX', -1),
config.ReadInt('Window/PosY', -1))
size = wx.Size(config.ReadInt('Window/Width', -1),
config.ReadInt('Window/Height', -1))
self.SetSize(size)
self.Move(pos)
rect = utils.AdjustRectToScreen(self.GetRect())
self.SetRect(rect)
perspective = config.Read('perspective', '')
if perspective:
try:
self.mgr.LoadPerspective(perspective)
except wx.PyAssertionError:
# ignore bad perspective string errors
pass
self.includeSizers = config.ReadBool('includeSizers', False)
def SaveSettings(self, config):
self.crust.SaveSettings(config)
self.info.SaveSettings(config)
if not self.IsIconized() and not self.IsMaximized():
w, h = self.GetSize()
config.WriteInt('Window/Width', w)
config.WriteInt('Window/Height', h)
px, py = self.GetPosition()
config.WriteInt('Window/PosX', px)
config.WriteInt('Window/PosY', py)
perspective = self.mgr.SavePerspective()
config.Write('perspective', perspective)
config.WriteBool('includeSizers', self.includeSizers)
#---------------------------------------------------------------------------
# should inspection frame (and children) be includeed in the tree?
INCLUDE_INSPECTOR = True
USE_CUSTOMTREECTRL = False
if USE_CUSTOMTREECTRL:
import wx.lib.agw.customtreectrl as CT
TreeBaseClass = CT.CustomTreeCtrl
else:
TreeBaseClass = wx.TreeCtrl
class InspectionTree(TreeBaseClass):
"""
All of the widgets in the app, and optionally their sizers, are
loaded into this tree.
"""
def __init__(self, *args, **kw):
#s = kw.get('style', 0)
#kw['style'] = s | wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT
TreeBaseClass.__init__(self, *args, **kw)
self.roots = []
self.built = False
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelectionChanged)
self.toolFrame = wx.GetTopLevelParent(self)
if 'wxMac' in wx.PlatformInfo:
self.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
def BuildTree(self, startWidget, includeSizers=False, expandFrame=False):
if self.GetCount():
self.DeleteAllItems()
self.roots = []
self.built = False
realRoot = self.AddRoot('Top-level Windows')
for w in wx.GetTopLevelWindows():
if w is wx.GetTopLevelParent(self) and not INCLUDE_INSPECTOR:
continue
root = self._AddWidget(realRoot, w, includeSizers)
self.roots.append(root)
# Expand the subtree containing the startWidget, and select it.
if not startWidget or not isinstance(startWidget, wx.Window):
startWidget = wx.GetApp().GetTopWindow()
if expandFrame:
top = wx.GetTopLevelParent(startWidget)
topItem = self.FindWidgetItem(top)
if topItem:
self.ExpandAllChildren(topItem)
self.built = True
self.SelectObj(startWidget)
def _AddWidget(self, parentItem, widget, includeSizers):
text = self.GetTextForWidget(widget)
item = self.AppendItem(parentItem, text)
self.SetItemData(item, widget)
# Add the sizer and widgets in the sizer, if we're showing them
widgetsInSizer = []
if includeSizers and widget.GetSizer() is not None:
widgetsInSizer = self._AddSizer(item, widget.GetSizer())
# Add any children not in the sizer, or all children if we're
# not showing the sizers
for child in widget.GetChildren():
if (not child in widgetsInSizer and
(not child.IsTopLevel() or
isinstance(child, wx.PopupWindow))):
self._AddWidget(item, child, includeSizers)
return item
def _AddSizer(self, parentItem, sizer):
widgets = []
text = self.GetTextForSizer(sizer)
item = self.AppendItem(parentItem, text)
self.SetItemData(item, sizer)
self.SetItemTextColour(item, "blue")
for si in sizer.GetChildren():
if si.IsWindow():
w = si.GetWindow()
self._AddWidget(item, w, True)
widgets.append(w)
elif si.IsSizer():
ss = si.GetSizer()
widgets += self._AddSizer(item, ss)
ss._parentSizer = sizer
else:
i = self.AppendItem(item, "Spacer")
self.SetItemData(i, si)
self.SetItemTextColour(i, "blue")
return widgets
def FindWidgetItem(self, widget):
"""
Find the tree item for a widget.
"""
for item in self.roots:
found = self._FindWidgetItem(widget, item)
if found:
return found
return None
def _FindWidgetItem(self, widget, item):
if self.GetItemData(item) is widget:
return item
child, cookie = self.GetFirstChild(item)
while child:
found = self._FindWidgetItem(widget, child)
if found:
return found
child, cookie = self.GetNextChild(item, cookie)
return None
def GetTextForWidget(self, widget):
"""
Returns the string to be used in the tree for a widget
"""
if hasattr(widget, 'GetName'):
return "%s (\"%s\")" % (widget.__class__.__name__, widget.GetName())
return widget.__class__.__name__
def GetTextForSizer(self, sizer):
"""
Returns the string to be used in the tree for a sizer
"""
return "%s" % sizer.__class__.__name__
def SelectObj(self, obj):
item = self.FindWidgetItem(obj)
if item:
self.EnsureVisible(item)
self.SelectItem(item)
def OnSelectionChanged(self, evt):
item = evt.GetItem()
if item:
obj = self.GetItemData(item)
self.toolFrame.SetObj(obj)
#---------------------------------------------------------------------------
class InspectionInfoPanel(wx.stc.StyledTextCtrl):
"""
Used to display information about the currently selected items.
Currently just a read-only :class:`stc.StyledTextCtrl` with some plain
text. Should probably add some styles to make things easier to
read.
"""
def __init__(self, *args, **kw):
wx.stc.StyledTextCtrl.__init__(self, *args, **kw)
from wx.py.editwindow import FACES
self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT,
"face:%(mono)s,size:%(size)d,back:%(backcol)s" % FACES)
self.StyleClearAll()
self.SetReadOnly(True)
self.SetMarginType(1, 0)
self.SetMarginWidth(1, 0)
self.SetSelForeground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
self.SetSelBackground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
def LoadSettings(self, config):
zoom = config.ReadInt('View/Zoom/Info', 0)
self.SetZoom(zoom)
def SaveSettings(self, config):
config.WriteInt('View/Zoom/Info', self.GetZoom())
def UpdateInfo(self, obj):
st = []
if not obj:
st.append("Item is None or has been destroyed.")
elif isinstance(obj, wx.Window):
st += self.FmtWidget(obj)
elif isinstance(obj, wx.Sizer):
st += self.FmtSizer(obj)
elif isinstance(obj, wx.SizerItem):
st += self.FmtSizerItem(obj)
self.SetReadOnly(False)
self.SetText('\n'.join(st))
self.SetReadOnly(True)
def Fmt(self, name, value):
if isinstance(value, six.string_types):
return " %s = '%s'" % (name, value)
else:
return " %s = %s" % (name, value)
def FmtWidget(self, obj):
def _countChildren(children):
count = 0
for child in children:
if not child.IsTopLevel():
count += 1
count += _countChildren(child.GetChildren())
return count
def _countAllChildren(children):
count = 0
for child in children:
count += 1
count += _countAllChildren(child.GetChildren())
return count
count = len([c for c in obj.GetChildren() if not c.IsTopLevel()])
rcount = _countChildren(obj.GetChildren())
tlwcount = _countAllChildren(obj.GetChildren())
st = ["Widget:"]
if hasattr(obj, 'GetName'):
st.append(self.Fmt('name', obj.GetName()))
st.append(self.Fmt('class', obj.__class__))
st.append(self.Fmt('bases', obj.__class__.__bases__))
st.append(self.Fmt('module', inspect.getmodule(obj)))
if hasattr(obj, 'this'):
st.append(self.Fmt('this', repr(obj.this)))
st.append(self.Fmt('id', obj.GetId()))
st.append(self.Fmt('style', obj.GetWindowStyle()))
st.append(self.Fmt('pos', obj.GetPosition()))
st.append(self.Fmt('size', obj.GetSize()))
st.append(self.Fmt('minsize', obj.GetMinSize()))
st.append(self.Fmt('bestsize', obj.GetBestSize()))
st.append(self.Fmt('client size', obj.GetClientSize()))
st.append(self.Fmt('virtual size',obj.GetVirtualSize()))
st.append(self.Fmt('IsEnabled', obj.IsEnabled()))
st.append(self.Fmt('IsShown', obj.IsShown()))
st.append(self.Fmt('IsFrozen', obj.IsFrozen()))
st.append(self.Fmt('fg color', obj.GetForegroundColour()))
st.append(self.Fmt('bg color', obj.GetBackgroundColour()))
st.append(self.Fmt('label', obj.GetLabel()))
if hasattr(obj, 'GetTitle'):
st.append(self.Fmt('title', obj.GetTitle()))
if hasattr(obj, 'GetValue'):
try:
st.append(self.Fmt('value', obj.GetValue()))
except Exception:
pass
st.append(' child count = %d (direct) %d (recursive) %d (include TLWs)' %
(count, rcount, tlwcount))
if obj.GetContainingSizer() is not None:
st.append('')
sizer = obj.GetContainingSizer()
st += self.FmtSizerItem(sizer.GetItem(obj))
return st
def FmtSizerItem(self, obj):
if obj is None:
return ['SizerItem: None']
st = ['SizerItem:']
st.append(self.Fmt('proportion', obj.GetProportion()))
st.append(self.Fmt('flag',
FlagsFormatter(itemFlags, obj.GetFlag())))
st.append(self.Fmt('border', obj.GetBorder()))
st.append(self.Fmt('pos', obj.GetPosition()))
st.append(self.Fmt('size', obj.GetSize()))
st.append(self.Fmt('minsize', obj.GetMinSize()))
st.append(self.Fmt('ratio', obj.GetRatio()))
st.append(self.Fmt('IsWindow', obj.IsWindow()))
st.append(self.Fmt('IsSizer', obj.IsSizer()))
st.append(self.Fmt('IsSpacer', obj.IsSpacer()))
st.append(self.Fmt('IsShown', obj.IsShown()))
if isinstance(obj, wx.GBSizerItem):
st.append(self.Fmt('cellpos', obj.GetPos()))
st.append(self.Fmt('cellspan', obj.GetSpan()))
st.append(self.Fmt('endpos', obj.GetEndPos()))
return st
def FmtSizer(self, obj):
st = ['Sizer:']
st.append(self.Fmt('class', obj.__class__))
if hasattr(obj, 'this'):
st.append(self.Fmt('this', repr(obj.this)))
st.append(self.Fmt('pos', obj.GetPosition()))
st.append(self.Fmt('size', obj.GetSize()))
st.append(self.Fmt('minsize', obj.GetMinSize()))
if isinstance(obj, wx.BoxSizer):
st.append(self.Fmt('orientation',
FlagsFormatter(orientFlags, obj.GetOrientation())))
if isinstance(obj, wx.GridSizer):
st.append(self.Fmt('cols', obj.GetCols()))
st.append(self.Fmt('rows', obj.GetRows()))
st.append(self.Fmt('vgap', obj.GetVGap()))
st.append(self.Fmt('hgap', obj.GetHGap()))
if isinstance(obj, wx.FlexGridSizer):
st.append(self.Fmt('rowheights', obj.GetRowHeights()))
st.append(self.Fmt('colwidths', obj.GetColWidths()))
st.append(self.Fmt('flexdir',
FlagsFormatter(orientFlags, obj.GetFlexibleDirection())))
st.append(self.Fmt('nonflexmode',
FlagsFormatter(flexmodeFlags, obj.GetNonFlexibleGrowMode())))
if isinstance(obj, wx.GridBagSizer):
st.append(self.Fmt('emptycell', obj.GetEmptyCellSize()))
if hasattr(obj, '_parentSizer'):
st.append('')
st += self.FmtSizerItem(obj._parentSizer.GetItem(obj))
return st
class FlagsFormatter(object):
def __init__(self, d, val):
self.d = d
self.val = val
def __str__(self):
st = []
for k in self.d.keys():
if self.val & k:
st.append(self.d[k])
if st:
return '|'.join(st)
else:
return '0'
orientFlags = {
wx.HORIZONTAL : 'wx.HORIZONTAL',
wx.VERTICAL : 'wx.VERTICAL',
}
itemFlags = {
wx.TOP : 'wx.TOP',
wx.BOTTOM : 'wx.BOTTOM',
wx.LEFT : 'wx.LEFT',
wx.RIGHT : 'wx.RIGHT',
# wx.ALL : 'wx.ALL',
wx.EXPAND : 'wx.EXPAND',
# wx.GROW : 'wx.GROW',
wx.SHAPED : 'wx.SHAPED',
wx.STRETCH_NOT : 'wx.STRETCH_NOT',
# wx.ALIGN_CENTER : 'wx.ALIGN_CENTER',
wx.ALIGN_LEFT : 'wx.ALIGN_LEFT',
wx.ALIGN_RIGHT : 'wx.ALIGN_RIGHT',
wx.ALIGN_TOP : 'wx.ALIGN_TOP',
wx.ALIGN_BOTTOM : 'wx.ALIGN_BOTTOM',
wx.ALIGN_CENTER_VERTICAL : 'wx.ALIGN_CENTER_VERTICAL',
wx.ALIGN_CENTER_HORIZONTAL : 'wx.ALIGN_CENTER_HORIZONTAL',
wx.ADJUST_MINSIZE : 'wx.ADJUST_MINSIZE',
wx.FIXED_MINSIZE : 'wx.FIXED_MINSIZE',
}
flexmodeFlags = {
wx.FLEX_GROWMODE_NONE : 'wx.FLEX_GROWMODE_NONE',
wx.FLEX_GROWMODE_SPECIFIED : 'wx.FLEX_GROWMODE_SPECIFIED',
wx.FLEX_GROWMODE_ALL : 'wx.FLEX_GROWMODE_ALL',
}
#---------------------------------------------------------------------------
class _InspectionHighlighter(object):
"""
All the highlighting code. A separate class to help reduce the
clutter in InspectionFrame.
"""
# should non TLWs be flashed too? Otherwise use a highlight rectangle
flashAll = False
color1 = 'red' # for widgets and sizers
color2 = 'red' # for item boundaries in sizers
color3 = '#00008B' # for items in sizers
highlightTime = 3000 # how long to display the highlights
# how to draw it
useOverlay = 'wxMac' in wx.PlatformInfo
def __init__(self):
if self.useOverlay:
self.overlay = wx.Overlay()
def HighlightCurrentItem(self, tree):
"""
Draw a highlight rectangle around the item represented by the
current tree selection.
"""
item = tree.GetSelection()
obj = tree.GetItemData(item)
if isinstance(obj, wx.Window):
self.HighlightWindow(obj)
elif isinstance(obj, wx.Sizer):
self.HighlightSizer(obj)
elif isinstance(obj, wx.SizerItem): # Spacer
pItem = tree.GetItemParent(item)
sizer = tree.GetItemData(pItem)
self.HighlightSizerItem(obj, sizer)
else:
raise RuntimeError("unknown object type: %s" % obj.__class__.__name__)
def HighlightWindow(self, win):
rect = win.GetRect()
tlw = win.GetTopLevelParent()
if self.flashAll or tlw is win:
self.FlickerTLW(win)
return
else:
pos = self.FindHighlightPos(tlw, win.ClientToScreen((0,0)))
rect.SetPosition(pos)
self.DoHighlight(tlw, rect, self.color1)
def HighlightSizerItem(self, item, sizer, penWidth=2):
win = sizer.GetContainingWindow()
tlw = win.GetTopLevelParent()
rect = item.GetRect()
pos = rect.GetPosition()
pos = self.FindHighlightPos(tlw, win.ClientToScreen(pos))
rect.SetPosition(pos)
if rect.width < 1: rect.width = 1
if rect.width < 1: rect.width = 1
self.DoHighlight(tlw, rect, self.color1, penWidth)
def HighlightSizer(self, sizer):
# first do the outline of the whole sizer like normal
win = sizer.GetContainingWindow()
tlw = win.GetTopLevelParent()
pos = sizer.GetPosition()
pos = self.FindHighlightPos(tlw, win.ClientToScreen(pos))
rect = wx.Rect(pos, sizer.GetSize())
dc, dco = self.DoHighlight(tlw, rect, self.color1)
# Now highlight the actual items within the sizer. This may
# get overdrawn by the code below for item boundaries, but if
# there is border padding then this will help make it more
# obvious.
dc.SetPen(wx.Pen(self.color3, 1))
for item in sizer.GetChildren():
if item.IsShown():
if item.IsWindow():
r = item.GetWindow().GetRect()
elif item.IsSizer():
p = item.GetSizer().GetPosition()
s = item.GetSizer().GetSize()
r = wx.Rect(p,s)
else:
continue
r = self.AdjustRect(tlw, win, r)
dc.DrawRectangle(r)
# Next highlight the area allocated to each item in the sizer.
# Each kind of sizer will need to be done a little
# differently.
dc.SetPen(wx.Pen(self.color2, 1))
if isinstance(sizer, wx.WrapSizer):
for item in sizer.GetChildren():
ir = self.AdjustRect(tlw, win, item.Rect)
dc.DrawRectangle(ir)
# wx.BoxSizer, wx.StaticBoxSizer
elif isinstance(sizer, wx.BoxSizer):
# NOTE: we have to do some reverse-engineering here for
# borders because the sizer and sizer item don't give us
# enough information to know for sure where item
# (allocated) boundaries are, just the boundaries of the
# actual widgets. TODO: It would be nice to add something
# to wx.SizerItem that would give us the full bounds, but
# that will have to wait until 2.9...
x, y = rect.GetPosition()
if sizer.Orientation == wx.HORIZONTAL:
y1 = y + rect.height
for item in sizer.GetChildren():
ir = self.AdjustRect(tlw, win, item.Rect)
x = ir.x
if item.Flag & wx.LEFT:
x -= item.Border
dc.DrawLine(x, y, x, y1)
if item.IsSizer():
dc.DrawRectangle(ir)
if sizer.Orientation == wx.VERTICAL:
x1 = x + rect.width
for item in sizer.GetChildren():
ir = self.AdjustRect(tlw, win, item.Rect)
y = ir.y
if item.Flag & wx.TOP:
y -= item.Border
dc.DrawLine(x, y, x1, y)
if item.IsSizer():
dc.DrawRectangle(ir)
# wx.FlexGridSizer, wx.GridBagSizer
elif isinstance(sizer, wx.FlexGridSizer):
sizer.Layout()
y = rect.y
for rh in sizer.RowHeights[:-1]:
y += rh
dc.DrawLine(rect.x, y, rect.x+rect.width, y)
y+= sizer.VGap
dc.DrawLine(rect.x, y, rect.x+rect.width, y)
x = rect.x
for cw in sizer.ColWidths[:-1]:
x += cw
dc.DrawLine(x, rect.y, x, rect.y+rect.height)
x+= sizer.HGap
dc.DrawLine(x, rect.y, x, rect.y+rect.height)
# wx.GridSizer
elif isinstance(sizer, wx.GridSizer):
# NOTE: More reverse engineering (see above.) This time we
# need to determine what the sizer is using for row
# heights and column widths.
#rh = cw = 0
#for item in sizer.GetChildren():
# rh = max(rh, item.Size.height)
# cw = max(cw, item.Size.width)
cw = (rect.width - sizer.HGap*(sizer.Cols-1)) / sizer.Cols
rh = (rect.height - sizer.VGap*(sizer.Rows-1)) / sizer.Rows
y = rect.y
for i in range(sizer.Rows-1):
y += rh
dc.DrawLine(rect.x, y, rect.x+rect.width, y)
y+= sizer.VGap
dc.DrawLine(rect.x, y, rect.x+rect.width, y)
x = rect.x
for i in range(sizer.Cols-1):
x += cw
dc.DrawLine(x, rect.y, x, rect.y+rect.height)
x+= sizer.HGap
dc.DrawLine(x, rect.y, x, rect.y+rect.height)
# Anything else is probably a custom sizer, just highlight the items
else:
del dc, odc
for item in sizer.GetChildren():
self.HighlightSizerItem(item, sizer, 1)
def FindHighlightPos(self, tlw, pos):
if self.useOverlay:
# We'll be using a ClientDC in this case so adjust the
# position accordingly
pos = tlw.ScreenToClient(pos)
return pos
def AdjustRect(self, tlw, win, rect):
pos = self.FindHighlightPos(tlw, win.ClientToScreen(rect.Position))
rect.Position = pos
return wx.Rect(pos, rect.Size)
def DoHighlight(self, tlw, rect, colour, penWidth=2):
if not tlw.IsFrozen():
tlw.Freeze()
if self.useOverlay:
dc = wx.ClientDC(tlw)
dco = wx.DCOverlay(self.overlay, dc)
dco.Clear()
else:
dc = wx.ScreenDC()
dco = None
dc.SetPen(wx.Pen(colour, penWidth))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
drawRect = wx.Rect(*rect)
dc.DrawRectangle(drawRect)
drawRect.Inflate(2,2)
if not self.useOverlay:
pos = tlw.ScreenToClient(drawRect.GetPosition())
drawRect.SetPosition(pos)
wx.CallLater(self.highlightTime, self.DoUnhighlight, tlw, drawRect)
return dc, dco
def DoUnhighlight(self, tlw, rect):
if not tlw:
return
if tlw.IsFrozen():
tlw.Thaw()
if self.useOverlay:
dc = wx.ClientDC(tlw)
dco = wx.DCOverlay(self.overlay, dc)
dco.Clear()
del dc, dco
self.overlay.Reset()
else:
tlw.RefreshRect(rect)
def FlickerTLW(self, tlw):
"""
Use a timer to alternate a TLW between shown and hidded state a
few times. Use to highlight a TLW since drawing and clearing an
outline is trickier.
"""
self.flickerCount = 0
tlw.Hide()
self.cl = wx.CallLater(300, self._Toggle, tlw)
def _Toggle(self, tlw):
if tlw.IsShown():
tlw.Hide()
self.cl.Restart()
else:
tlw.Show()
self.flickerCount += 1
if self.flickerCount < 4:
self.cl.Restart()
#---------------------------------------------------------------------------
from wx.lib.embeddedimage import PyEmbeddedImage
Refresh = PyEmbeddedImage(
"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"
"GXlKb/xX4wAAAABJRU5ErkJggg==")
Find = PyEmbeddedImage(
"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")
ShowSizers = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABChJ"
"REFUSIm1lU1oXFUUx3/33nn3zbx5mcm0M5OZxGlNAn6XUqlRKIIWKrXqRlCLOwvitrooqKDg"
"RhcirkSpigSECl0WBa104UKoSIoLS8TWlja1NTOTyUzz5s37uNdFWpumJSktHriry//8zj38"
"z7nwP4dY5/7BUqn0quu62621i9cJhSjGcTzTarUOAr/dFn1sbGy/N+SdksgoQ2bh2mFBQpTz"
"vdP1ev3AWjkya10qpe4yPbPZ3GeUecEMswQoYK4I3w+wzWiz3qgbtw0AQoHoswWfNxyYLYE2"
"8GcVfr8IzU5gjOnfCWA5YuCvHPw0CRkLhGDjW5LeGiAFWjGcaYKyUHLAwvoeWQcgpczZjM3z"
"A3CiD5fPAlikFXRicNy8lNJbK4das/A0VdKVJd/4oWqJZhSGVcJUeH3n5JA/NGe1OhEG/W+i"
"KJq9LUAURe1KuVJQrrqnn4YVrXXkOE5gFCpf8NteLvdts9k8AgRXJDf0bC1ArlgsTiVJsqfX"
"6+1K03RQLpenfd//tdvtbh0MBvdaaxe01pcGg8FFlq1wU8jNoqiUeq5Wqx3SWlutdWvTpk3v"
"ANuBbY1G423Xdecdx7EjIyOHlVLPA6X1kl4lK6XU7rGxsU+UUkue54XlcvlL4LEVL36kUql8"
"ns/nl6SUwejo6EGl1DNcM81/r7ihRfl8fmehUHil2Wzu1Vr3Xdc9lCTJZ4PB4DhXzAlcAOa0"
"1koIMdntdqdKpZInhGjHcXx6ZT4FjDQajR21Wm2L7/sPJ0nyYr/ff1ZKOV8ul2eTJDnS6XSO"
"sjwN4mp1cRw3s9lssVQq1cIwVEmSbNVa+9VqNTsyMjLh+/744uJiT3ied8BxnJcx6QMS0wkH"
"UaEfJe6w4mc8v5AINWOS+KMgCGZWVuZ53taMlPtRaqovTRAv9LaTdQIn5y0oYzZkkaejOJ6m"
"Xq8fk0IkCGVxci2UnsfNz1MatSDtUN7rT0xMvLa6lePj4/v8wlAfsCVUuJFMZxh1eQMqyIFF"
"irRerx/LGGMCYa3ioV1f8el30/w4k8V178bNfMjHL3mcPREA0U1MES3ZNK2R6Z9katpgkpBU"
"jFLovcnJRz8QF54wxgTXVoUQl9iBzz/bniQlT39JIdecQywICfEwQwFkd4KqdAmDS8gKEMLK"
"XZTaOVImsbyOpM1QXiPkmgBAZBApeOEsS5OSjD8gJYsG6AFkhBA5C3D6+G72vudTGYXg8gYQ"
"0J6DDB4sK67LLISjhXQ6xLm3+OXxU6RuBGxEM0sPLFkhRC5jjDmntD5D2N4jDr8LsMCV+bCQ"
"t8Xhs0mStFYD4jhu55B/LEpx//vi/A5gieWdJLBoV+m2MeacAJ6uVqt7pZQNa+11v5MQohAE"
"wdFut/sFcH4VY9T3/X2+7z9lre2t0mXTNP17fn7+6/V6fMfxL1klnkaQRVDaAAAAAElFTkSu"
"QmCC")
ShowFilling = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAZ5J"
"REFUSIntlc9KAkEcxz+tu7OxLRSuGUXtIfUpukW9QJcO3nqELoLXLr2Bt47dRBAqgxJ6BqMw"
"PJe0FEliaLNrFxV1zXatY9/TMDPf72eG3/yBf/2guaH2DrALrADtGfME8AKUgCsAdWhwX1XV"
"bSnlMtCZEaApivrqeXJxEmBDSrl+dHQoNjf3OD9/xjSDJ5vmMre3Z1xeHi8AG/3+YcAH8GEY"
"SbG6uoVtP2IYwQFLS2s8PT0MciYBALi5uUfTrrEsB88LDtB1C027A+h+N6cAvBUK+e6surg4"
"6wLvvSwAlHFKu/0ZfNkBvD6AEGJmwCSvrwaNRgOAZrMZKtw0zYF3KiCXy1Eul2m1WqEAhmFQ"
"q9VgrMg+QKVSoVqt4oU5QoCiKHQ6/vvpA2QyGdLpNPV6PRQgHo+Tz+fJZrPTAYlEgmQyiW3b"
"oQBCCFKpFIy+b36ArusDQ1j1vCM18B3TaDQaOrgvy7Jgyg7mgflisQiA4zihwmOxGKVSCUDv"
"ZTFOO/mL5zoSiby6rnsFHMDoDk6llA6//HBc1+1/OP8Kpi8497f1tG0HzQAAAABJRU5ErkJg"
"gg==")
Icon = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAALhJ"
"REFUWIXtl80SgyAMhHeR99Y+eJseLAdHbJM0TjiwN2dI+MgfQpYFmSqpu0+AEQDqrwXyeorH"
"McvCEIBdm3F7/fr0FKgBRFaIrHkAdykdQFmEGm2HL233BAIAYmxYEqjePo9SBYBvBKppclDz"
"prMcqAhbAtknJx+3AKRHgGhnv4iApQY+jtSWpOY27BnifNt5uyk9BekAoZNwl21yDBSBi/63"
"yOMiLAXaf8AuwP9n94vzaTYBsgHeht4lXXmb7yQAAAAASUVORK5CYII=")
CollapseTree = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAf9J"
"REFUSIm9lD9r20AYhx9Fxe5SKDIynVJodFMaChkC6eBCJ+WWdulSb1o6RR8gH6AfQF5aSksX"
"aQ0eajwVkinO1MFk0YXg0uLSISZDoQhcddAf5MR241buD4670/tyD+/vXh0sWdqUb1vAQ2Bj"
"Suwb0E7Xx38DaAKPgTuAnJLfSSEAn4DWIoAm8ByQruti2zYAlmUBoJSi2+3ieV4R1v0TJANs"
"AS8Ap9PpYFkWUSSuJFcqIUopAKSUAO+A18yxS0/nZ8AD13WFbdtEkWB9Hep1ODqC0QgMA8bj"
"GqYJhmGgaRq9Xm8I3AY+zgKspPMGIIuHZ6qn4/Q02UeRIIpEZqEkua+ZWpkXLEM3ipvEe9C0"
"ad2bqN+P89zr6P9WoJRidVXQ78e55/U09h1YW5vMTTWcB8i66B7wq1arie1ti/G4hmEknfNl"
"BD8Kh1cqIbp+ThAEVKtVBoNBCziZBfjX/wDgEHg0C7D0O8gs+grcAm76vi80TcM04eJCYZqg"
"6+ecnR0TBAGu6+L7PlJKhAgJQ+6SvF/vrwNsAm+BD0B8eTQajXztOMT7HnFrL48fpGNCizzX"
"Q5IXdDfdN/Y92Hna5s2rJ+y+zPPm3skiOoCkip+f23Fr70o1pWgCkoGKkKV3URnKqyjaxTKs"
"omCX4+SQ0pS1aew4xJub5VZwGZQdfv83yOfTR/iA1xwAAAAASUVORK5CYII=")
ExpandTree = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAepJ"
"REFUSIm1lDFr20AYhp+ri+iSxUUhW4dIU9wlQ4YOKnRSj4K7Z9NSCFg/ID+gP0BZWkpLF3kN"
"Giq0pkOpMxWSTFIIFIqHQCFbUQnXQTpFslXXptYLx90nvdzD9913Bx1LtHzbA54Aj1v+nQFf"
"yvXpMoD7M/E+8AzYAmSLP66BbSBcBTACXED6vo/rugBYlgVAlmUkSSKDIND+rXJeCNEl2gNe"
"AV4cx1iWRZ7bc2bDSMmyDAApJcAH4C0LytUr5wPA8n3fdl2XPLfZ2YHNTbi+vjPf3j7ENKHf"
"7yOEYDKZTIHfwNe/Ae7V0pX1zbUGA8FgILi8LOI8t8lzW5dQ0t4Mc4DO1ADoA724ACEEQtx1"
"8XBYZDLrXQnQhRoA3SEAUaSIItWIz89Vm3e6DOAMiJMkwTBSALa3i6Gl14aRYhgpSZLgOA7A"
"t0WA/70HAJ+Bp//KoDPpi/YD2AAehGFoCyEwTbi5yTBN6PV+cnV1yng8xvd9wjBESoltp6Qp"
"jyjer4/LAPeB98AnQM0Ox3GqteehjgPU0WH1/6QcDa3yXE8pDnRUxs5xAM9fRrx7M2T0uvIt"
"PJNVdAJFFr++R+rocC6btagB0aA6pPMuWoeqLOrlootSUSuX51WQtUm3qfI81O7uejOYBenN"
"X/wBVz/ONKbGYPkAAAAASUVORK5CYII=")
HighlightItem = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAQZJ"
"REFUSInFlTGSgjAUhv8XuIRl9ga0XgmuoI5abwfH8Ai22GlJZ0otuQD5t3DZ1V1CwgzgP8OQ"
"QCZfkv+9FxEVYUrFbYO2oW+wqEiGAtRzhyRIQh9eH+RXAMBmvfIuohcwheLnTnZ6vM3NjAaQ"
"1mTahvrAHwCzj+BJVI83sesHAMjRM3OVgNkFm/WK292+EzKvB86zr5Lu76b2AubdAbqMda0+"
"UOIqFdY2lKMHYGrw06DL3Tbrxzmi/Iq0JNLyO/Pxm/Uze/BXVRIUKajvKM6AXuh/kfjeHTC7"
"TAdw1RfahmlJFOewgtjvQY/0QgeNe3MUOVQsw2/OwQBRkQy5Op2lYixN7sEXVhRd4PXVHvwA"
"AAAASUVORK5CYII=")
EvtWatcher = PyEmbeddedImage(
"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")