Preferences for default "sizeritem" parameters for new panels and
controls can be configured ("File">"Preferences...").
Implemented comment object for including simple one-line comments and
comment directives as tree nodes. No validation is performed for a
valid XML string so comments must not contain "-->". Comment directive
is a special comment starting with '%' character, followed by a line
of python code. It is executed using 'exec' when the resource file is
opened. This is useful to import plugin modules containing custom
handlers which are specific to the resource file, hovewer this is of
course a security hole if you use foreign XRC files. A warning is
displayed if the preference option 'ask' is selected (by default).
Added support for custom controls and plugin modules. Refer to this
wxPythonWiki for the details:
http://wiki.wxpython.org/index.cgi/XRCed#custom
Tool panel sections can be collapsed/expanded by clicking on the
label of a tool group.
Some undo/redo and other fixes.
Fixes for wxMSW (notebook highlighting, control sizes, tree Unselect).
Notebook page highlighting fix. Highlight resizes when the window
is resized. ParamUnit spin button detects event handler re-entry
(wxGTK probably has a bug in wxSpinButton with repeated events).
Fix for dealing with empty 'growable' property, using MiniFrame
for properties panel, the panel is restored together with the
main window.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@44949 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
from xml.dom import minidom
|
||||
from globals import *
|
||||
from params import *
|
||||
import traceback, types
|
||||
|
||||
# Base class for interface parameter classes
|
||||
class xxxNode:
|
||||
@@ -189,6 +190,7 @@ class xxxObject:
|
||||
hasStyle = True # almost everyone
|
||||
hasName = True # has name attribute?
|
||||
isSizer = hasChild = False
|
||||
isElement = True
|
||||
allParams = None # Some nodes have no parameters
|
||||
# Style parameters (all optional)
|
||||
styles = ['fg', 'bg', 'font', 'enabled', 'focused', 'hidden', 'tooltip']
|
||||
@@ -210,7 +212,7 @@ class xxxObject:
|
||||
# parent is parent xxx object (or None if none), element is DOM element object
|
||||
def __init__(self, parent, element, refElem=None):
|
||||
self.parent = parent
|
||||
self.element = element
|
||||
self.node = element
|
||||
self.refElem = refElem
|
||||
self.undo = None
|
||||
# Reference are dereferenced
|
||||
@@ -230,33 +232,32 @@ class xxxObject:
|
||||
if self.hasName: self.name = element.getAttribute('name')
|
||||
# Set parameters (text element children)
|
||||
self.params = {}
|
||||
nodes = element.childNodes[:]
|
||||
for node in nodes:
|
||||
if node.nodeType == minidom.Node.ELEMENT_NODE:
|
||||
tag = node.tagName
|
||||
for n in element.childNodes[:]:
|
||||
if n.nodeType == minidom.Node.ELEMENT_NODE:
|
||||
tag = n.tagName
|
||||
if tag in ['object', 'object_ref']:
|
||||
continue # do nothing for object children here
|
||||
elif tag not in self.allParams and tag not in self.styles:
|
||||
print 'WARNING: unknown parameter for %s: %s' % \
|
||||
(self.className, tag)
|
||||
elif tag in self.specials:
|
||||
self.special(tag, node)
|
||||
self.special(tag, n)
|
||||
elif tag == 'content':
|
||||
if self.className == 'wxCheckListBox':
|
||||
self.params[tag] = xxxParamContentCheckList(node)
|
||||
self.params[tag] = xxxParamContentCheckList(n)
|
||||
else:
|
||||
self.params[tag] = xxxParamContent(node)
|
||||
self.params[tag] = xxxParamContent(n)
|
||||
elif tag == 'font': # has children
|
||||
self.params[tag] = xxxParamFont(element, node)
|
||||
self.params[tag] = xxxParamFont(element, n)
|
||||
elif tag in self.bitmapTags:
|
||||
# Can have attributes
|
||||
self.params[tag] = xxxParamBitmap(node)
|
||||
self.params[tag] = xxxParamBitmap(n)
|
||||
else: # simple parameter
|
||||
self.params[tag] = xxxParam(node)
|
||||
elif node.nodeType == minidom.Node.TEXT_NODE and node.data.isspace():
|
||||
self.params[tag] = xxxParam(n)
|
||||
elif n.nodeType == minidom.Node.TEXT_NODE and n.data.isspace():
|
||||
# Remove empty text nodes
|
||||
element.removeChild(node)
|
||||
node.unlink()
|
||||
element.removeChild(n)
|
||||
n.unlink()
|
||||
|
||||
# Check that all required params are set
|
||||
for param in self.required:
|
||||
@@ -281,9 +282,9 @@ class xxxObject:
|
||||
break
|
||||
if found:
|
||||
nextTextElem = self.params[p].node
|
||||
self.element.insertBefore(elem, nextTextElem)
|
||||
self.node.insertBefore(elem, nextTextElem)
|
||||
else:
|
||||
self.element.appendChild(elem)
|
||||
self.node.appendChild(elem)
|
||||
else:
|
||||
wx.LogWarning('Required parameter %s of %s missing' %
|
||||
(param, self.className))
|
||||
@@ -313,7 +314,34 @@ class xxxObject:
|
||||
if self.hasChild: obj = self.child
|
||||
else: obj = self
|
||||
obj.name = name
|
||||
obj.element.setAttribute('name', name)
|
||||
obj.node.setAttribute('name', name)
|
||||
# Set normal (text) params
|
||||
def set(self, param, value):
|
||||
try:
|
||||
self.params[param].update(value)
|
||||
except KeyError:
|
||||
elem = g.tree.dom.createElement(param)
|
||||
p = xxxParam(elem)
|
||||
p.update(value)
|
||||
self.params[param] = p
|
||||
self.node.appendChild(elem)
|
||||
# Special processing for growablecols-like parameters
|
||||
# represented by several nodes
|
||||
def special(self, tag, node):
|
||||
if not self.params.has_key(tag):
|
||||
# Create new multi-group
|
||||
self.params[tag] = xxxParamMulti(node)
|
||||
self.params[tag].append(xxxParamInt(node))
|
||||
def setSpecial(self, param, value):
|
||||
# Straightforward implementation: remove, add again
|
||||
self.params[param].remove()
|
||||
del self.params[param]
|
||||
for i in value:
|
||||
node = g.tree.dom.createElement(param)
|
||||
text = g.tree.dom.createTextNode(str(i))
|
||||
node.appendChild(text)
|
||||
self.node.appendChild(node)
|
||||
self.special(param, node)
|
||||
|
||||
# Imitation of FindResource/DoFindResource from xmlres.cpp
|
||||
def DoFindResource(parent, name, classname, recursive):
|
||||
@@ -360,7 +388,7 @@ class xxxParamFont(xxxObject, xxxNode):
|
||||
self.data = v
|
||||
def update(self, value):
|
||||
# `value' is a list of strings corresponding to all parameters
|
||||
elem = self.element
|
||||
elem = self.node
|
||||
# Remove old elements first
|
||||
childNodes = elem.childNodes[:]
|
||||
for node in childNodes: elem.removeChild(node)
|
||||
@@ -638,6 +666,16 @@ class xxxDateCtrl(xxxObject):
|
||||
winStyles = ['wxDP_DEFAULT', 'wxDP_SPIN', 'wxDP_DROPDOWN',
|
||||
'wxDP_ALLOWNONE', 'wxDP_SHOWCENTURY']
|
||||
|
||||
class xxxGrid(xxxObject):
|
||||
allParams = ['pos', 'size', 'style']
|
||||
|
||||
class xxxFilePickerCtrl(xxxObject):
|
||||
allParams = ['value', 'message', 'wildcard', 'pos', 'size', 'style']
|
||||
winStyles = ['wxFLP_OPEN', 'wxFLP_SAVE', 'wxFLP_OVERWRITE_PROMPT',
|
||||
'wxFLP_FILE_MUST_EXIST', 'wxFLP_CHANGE_DIR',
|
||||
'wxFLP_DEFAULT_STYLE']
|
||||
|
||||
|
||||
################################################################################
|
||||
# Buttons
|
||||
|
||||
@@ -771,45 +809,11 @@ class xxxFlexGridSizer(xxxGridSizer):
|
||||
specials = ['growablecols', 'growablerows']
|
||||
allParams = ['cols', 'rows', 'vgap', 'hgap'] + specials
|
||||
paramDict = {'growablecols': ParamIntList, 'growablerows': ParamIntList}
|
||||
# Special processing for growable* parameters
|
||||
# (they are represented by several nodes)
|
||||
def special(self, tag, node):
|
||||
if not self.params.has_key(tag):
|
||||
# Create new multi-group
|
||||
self.params[tag] = xxxParamMulti(node)
|
||||
self.params[tag].append(xxxParamInt(node))
|
||||
def setSpecial(self, param, value):
|
||||
# Straightforward implementation: remove, add again
|
||||
self.params[param].remove()
|
||||
del self.params[param]
|
||||
for i in value:
|
||||
node = g.tree.dom.createElement(param)
|
||||
text = g.tree.dom.createTextNode(str(i))
|
||||
node.appendChild(text)
|
||||
self.element.appendChild(node)
|
||||
self.special(param, node)
|
||||
|
||||
class xxxGridBagSizer(xxxSizer):
|
||||
specials = ['growablecols', 'growablerows']
|
||||
allParams = ['vgap', 'hgap'] + specials
|
||||
paramDict = {'growablecols':ParamIntList, 'growablerows':ParamIntList}
|
||||
# Special processing for growable* parameters
|
||||
# (they are represented by several nodes)
|
||||
def special(self, tag, node):
|
||||
if not self.params.has_key(tag):
|
||||
# Create new multi-group
|
||||
self.params[tag] = xxxParamMulti(node)
|
||||
self.params[tag].append(xxxParamInt(node))
|
||||
def setSpecial(self, param, value):
|
||||
# Straightforward implementation: remove, add again
|
||||
self.params[param].remove()
|
||||
del self.params[param]
|
||||
for i in value:
|
||||
node = g.tree.dom.createElement(param)
|
||||
text = g.tree.dom.createTextNode(str(i))
|
||||
node.appendChild(text)
|
||||
self.element.appendChild(node)
|
||||
self.special(param, node)
|
||||
paramDict = {'growablecols': ParamIntList, 'growablerows': ParamIntList}
|
||||
|
||||
# Container with only one child.
|
||||
# Not shown in tree.
|
||||
@@ -843,7 +847,8 @@ class xxxChildContainer(xxxObject):
|
||||
class xxxSizerItem(xxxChildContainer):
|
||||
allParams = ['option', 'flag', 'border', 'minsize', 'ratio']
|
||||
paramDict = {'option': ParamInt, 'minsize': ParamPosSize, 'ratio': ParamPosSize}
|
||||
#default = {'cellspan': '1,1'}
|
||||
defaults_panel = {}
|
||||
defaults_control = {}
|
||||
def __init__(self, parent, element, refElem=None):
|
||||
# For GridBag sizer items, extra parameters added
|
||||
if isinstance(parent, xxxGridBagSizer):
|
||||
@@ -923,7 +928,143 @@ class xxxUnknown(xxxObject):
|
||||
winStyles = ['wxNO_FULL_REPAINT_ON_RESIZE']
|
||||
|
||||
################################################################################
|
||||
# Comment
|
||||
|
||||
_handlers = [] # custom handler classes/funcs
|
||||
def getHandlers():
|
||||
return _handlers
|
||||
def setHandlers(handlers):
|
||||
global _handlers
|
||||
_handlers = handlers
|
||||
_CFuncPtr = None # ctypes function type
|
||||
|
||||
def register(hndlr):
|
||||
"""Register hndlr function or XmlResourceHandler class."""
|
||||
if _CFuncPtr and isinstance(hndlr, _CFuncPtr):
|
||||
_handlers.append(hndlr)
|
||||
return
|
||||
if not isinstance(hndlr, type):
|
||||
wx.LogError('handler is not a type: %s' % hndlr)
|
||||
elif not issubclass(hndlr, wx.xrc.XmlResourceHandler):
|
||||
wx.LogError('handler is not a XmlResourceHandler: %s' % hndlr)
|
||||
else:
|
||||
_handlers.append(hndlr)
|
||||
|
||||
def load_dl(path, localname=''):
|
||||
"""Load shared/dynamic library into xxx namespace.
|
||||
|
||||
If path is not absolute or relative, try to find in the module's directory.
|
||||
"""
|
||||
if not localname:
|
||||
localname = os.path.basename(os.path.splitext(path)[0])
|
||||
try:
|
||||
import ctypes
|
||||
global _CFuncPtr
|
||||
_CFuncPtr = ctypes._CFuncPtr # use as a flag of loaded ctypes
|
||||
#if not os.path.dirname(path) and os.path.isfile(path):
|
||||
|
||||
except ImportError:
|
||||
wx.LogError('ctypes module not found')
|
||||
globals()[localname] = None
|
||||
return
|
||||
try:
|
||||
dl = ctypes.CDLL(path)
|
||||
globals()[localname] = dl
|
||||
# Register AddXmlHandlers() if exists
|
||||
try:
|
||||
register(dl.AddXmlHandlers)
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
wx.LogError('error loading dynamic library: %s' % path)
|
||||
print traceback.print_exc()
|
||||
|
||||
# Called when creating test window
|
||||
def addHandlers():
|
||||
for h in _handlers:
|
||||
if _CFuncPtr and isinstance(h, _CFuncPtr):
|
||||
try:
|
||||
apply(h, ())
|
||||
except:
|
||||
wx.LogError('error calling DL func: "%s"' % h)
|
||||
print traceback.print_exc()
|
||||
else:
|
||||
try:
|
||||
xrc.XmlResource.Get().AddHandler(apply(h, ()))
|
||||
except:
|
||||
wx.LogError('error adding XmlHandler: "%s"' % h)
|
||||
print traceback.print_exc()
|
||||
|
||||
def custom(klassName, klass='unknown'):
|
||||
"""Define custom control based on xrcClass.
|
||||
|
||||
klass: new object name
|
||||
xrcClass: name of an existing XRC object class or
|
||||
a class object defining class parameters.
|
||||
"""
|
||||
if type(klass) is str:
|
||||
# Copy correct xxx class under new name
|
||||
kl = xxxDict[klass]
|
||||
xxxClass = types.ClassType('xxx' + klassName, kl.__bases__, kl.__dict__)
|
||||
else:
|
||||
xxxClass = klass
|
||||
# Register param IDs
|
||||
for param in klass.allParams + klass.paramDict.keys():
|
||||
if not paramIDs.has_key(param):
|
||||
paramIDs[param] = wx.NewId()
|
||||
# Insert in dictionaty
|
||||
xxxDict[klassName] = xxxClass
|
||||
# Add to menu
|
||||
g.pullDownMenu.addCustom(klassName)
|
||||
|
||||
class xxxParamComment(xxxParam):
|
||||
locals = {} # namespace for comment directives
|
||||
allow = None # undefined initial state for current file
|
||||
def __init__(self, node):
|
||||
xxxNode.__init__(self, node)
|
||||
self.textNode = node
|
||||
# Parse "pragma" comments if enabled
|
||||
if node.data and node.data[0] == '%' and g.conf.allowExec != 'no' and \
|
||||
xxxParamComment.allow is not False:
|
||||
# Show warning
|
||||
if g.conf.allowExec == 'ask' and xxxParamComment.allow is None:
|
||||
flags = wx.ICON_EXCLAMATION | wx.YES_NO | wx.CENTRE
|
||||
dlg = wx.MessageDialog(g.frame, '''
|
||||
This file contains executable %comment directives. Allow to execute?''',
|
||||
'Warning', flags)
|
||||
say = dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
if say == wx.ID_YES:
|
||||
xxxParamComment.allow = True
|
||||
else:
|
||||
xxxParamComment.allow = False
|
||||
try:
|
||||
code = node.data[1:]
|
||||
exec code in globals(), self.locals
|
||||
except:
|
||||
wx.LogError('exec error: "%s"' % code)
|
||||
print traceback.print_exc()
|
||||
|
||||
class xxxComment(xxxObject):
|
||||
hasStyle = hasName = False
|
||||
allParams = required = ['comment']
|
||||
|
||||
def __init__(self, parent, node):
|
||||
self.parent = parent
|
||||
self.node = node
|
||||
self.isElement = False
|
||||
self.undo = None
|
||||
self.className = 'comment'
|
||||
self.ref = self.subclass = None
|
||||
self.params = {'comment': xxxParamComment(node)}
|
||||
|
||||
def treeName(self):
|
||||
# Replace newlines by \n to avoid tree item resizing
|
||||
return self.params['comment'].value().replace('\n', r'\n')
|
||||
|
||||
################################################################################
|
||||
|
||||
# Mapping of XRC names to xxx classes
|
||||
xxxDict = {
|
||||
'wxPanel': xxxPanel,
|
||||
'wxDialog': xxxDialog,
|
||||
@@ -973,6 +1114,8 @@ xxxDict = {
|
||||
'wxGenericDirCtrl': xxxGenericDirCtrl,
|
||||
'wxSpinCtrl': xxxSpinCtrl,
|
||||
'wxScrolledWindow': xxxScrolledWindow,
|
||||
'wxGrid': xxxGrid,
|
||||
'wxFilePickerCtrl': xxxFilePickerCtrl,
|
||||
'wxDatePickerCtrl': xxxDateCtrl,
|
||||
|
||||
'wxBoxSizer': xxxBoxSizer,
|
||||
@@ -990,13 +1133,15 @@ xxxDict = {
|
||||
'separator': xxxSeparator,
|
||||
|
||||
'unknown': xxxUnknown,
|
||||
'comment': xxxComment,
|
||||
}
|
||||
|
||||
# Create IDs for all parameters of all classes
|
||||
paramIDs = {'fg': wx.NewId(), 'bg': wx.NewId(), 'exstyle': wx.NewId(), 'font': wx.NewId(),
|
||||
'enabled': wx.NewId(), 'focused': wx.NewId(), 'hidden': wx.NewId(),
|
||||
'tooltip': wx.NewId(), 'encoding': wx.NewId(),
|
||||
'cellpos': wx.NewId(), 'cellspan': wx.NewId()
|
||||
'cellpos': wx.NewId(), 'cellspan': wx.NewId(),
|
||||
'text': wx.NewId()
|
||||
}
|
||||
for cl in xxxDict.values():
|
||||
if cl.allParams:
|
||||
@@ -1009,25 +1154,29 @@ for cl in xxxDict.values():
|
||||
|
||||
# Test for object elements
|
||||
def IsObject(node):
|
||||
return node.nodeType == minidom.Node.ELEMENT_NODE and node.tagName in ['object', 'object_ref']
|
||||
return node.nodeType == minidom.Node.ELEMENT_NODE and \
|
||||
node.tagName in ['object', 'object_ref'] or \
|
||||
node.nodeType == minidom.Node.COMMENT_NODE
|
||||
|
||||
# Make XXX object from some DOM object, selecting correct class
|
||||
def MakeXXXFromDOM(parent, element):
|
||||
if element.tagName == 'object_ref':
|
||||
ref = element.getAttribute('ref')
|
||||
def MakeXXXFromDOM(parent, node):
|
||||
if node.nodeType == minidom.Node.COMMENT_NODE:
|
||||
return xxxComment(parent, node)
|
||||
if node.tagName == 'object_ref':
|
||||
ref = node.getAttribute('ref')
|
||||
refElem = FindResource(ref)
|
||||
if refElem: cls = refElem.getAttribute('class')
|
||||
else: return xxxUnknown(parent, element)
|
||||
else: return xxxUnknown(parent, node)
|
||||
else:
|
||||
refElem = None
|
||||
cls = element.getAttribute('class')
|
||||
cls = node.getAttribute('class')
|
||||
try:
|
||||
klass = xxxDict[cls]
|
||||
except KeyError:
|
||||
# If we encounter a weird class, use unknown template
|
||||
print 'WARNING: unsupported class:', element.getAttribute('class')
|
||||
print 'WARNING: unsupported class:', node.getAttribute('class')
|
||||
klass = xxxUnknown
|
||||
return klass(parent, element, refElem)
|
||||
return klass(parent, node, refElem)
|
||||
|
||||
# Make empty DOM element
|
||||
def MakeEmptyDOM(className):
|
||||
@@ -1051,7 +1200,8 @@ def MakeEmptyDOM(className):
|
||||
def MakeEmptyXXX(parent, className):
|
||||
# Make corresponding DOM object first
|
||||
elem = MakeEmptyDOM(className)
|
||||
# If parent is a sizer, we should create sizeritem object, except for spacers
|
||||
# Special handling, e.g. if parent is a sizer, we should create
|
||||
# sizeritem object, except for spacers, etc.
|
||||
if parent:
|
||||
if parent.isSizer and className != 'spacer':
|
||||
sizerItemElem = MakeEmptyDOM(parent.itemTag)
|
||||
@@ -1070,7 +1220,16 @@ def MakeEmptyXXX(parent, className):
|
||||
pageElem.appendChild(elem)
|
||||
elem = pageElem
|
||||
# Now just make object
|
||||
return MakeXXXFromDOM(parent, elem)
|
||||
xxx = MakeXXXFromDOM(parent, elem)
|
||||
# Special defaults for new panels and controls
|
||||
if isinstance(xxx, xxxSizerItem):
|
||||
if isinstance(xxx.child, xxxContainer) and not xxx.child.isSizer:
|
||||
for param,v in xxxSizerItem.defaults_panel.items():
|
||||
xxx.set(param, v)
|
||||
elif isinstance(xxx.child, xxxObject):
|
||||
for param,v in xxxSizerItem.defaults_control.items():
|
||||
xxx.set(param, v)
|
||||
return xxx
|
||||
|
||||
# Make empty DOM element for reference
|
||||
def MakeEmptyRefDOM(ref):
|
||||
@@ -1107,3 +1266,15 @@ def MakeEmptyRefXXX(parent, ref):
|
||||
#xxx.allParams.remove('label')
|
||||
return xxx
|
||||
|
||||
# Make empty comment node
|
||||
def MakeEmptyCommentDOM():
|
||||
node = g.tree.dom.createComment('')
|
||||
return node
|
||||
|
||||
# Make empty xxxComment
|
||||
def MakeEmptyCommentXXX(parent):
|
||||
node = MakeEmptyCommentDOM()
|
||||
# Now just make object
|
||||
xxx = MakeXXXFromDOM(parent, node)
|
||||
return xxx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user