Add _xml and _xrc modules with some unit tests

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@73080 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2012-11-30 22:32:53 +00:00
parent 10ec2fb1f1
commit 4d5faa2868
11 changed files with 536 additions and 25 deletions

View File

@@ -139,8 +139,6 @@ other dev stuff
* Add _propdlg module
* Add _xrc module (containg xrc and xml classes)
* Add _richtext module
* Add _aui module ?? (or go with only agw aui)

View File

@@ -383,6 +383,19 @@ with a wx.ComboPopup in a has-a relationship rather than an is-a
relationship. See samples/combo/combo1.py for an example.
XRC
-------
The "LoadOnFoo" methods of the XmlResource class were renamed overloads of
the coresponding "LoadFoo" methods. Since we no longer need to rename
overloaded methods the "LoadOn" version has been removed and you should just
use the "LoadFoo" version instead. These methods are used to load some XRC
content onto an existing window, such as a Frame, instead of creating a new
Frame for the content.
.. toctree::
:maxdepth: 2
:hidden:

View File

@@ -79,6 +79,12 @@
<p class="biglink"><a class="biglink" href="{{ pathto("glcanvas.1classindex") }}">wx.glcanvas</a><br/>
<span class="linkdescr">Classes for embedding OpenGL views in a window</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("xrc.1classindex") }}">wx.xrc</a><br/>
<span class="linkdescr">Classes for loading widgets and layout from XML</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("xml.1classindex") }}">wx.xml</a><br/>
<span class="linkdescr">Some simple XML classes for use with XRC</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("py") }}">wx.py</a><br/>
<span class="linkdescr">The py package, containing PyCrust and related modules</span></p>

99
etg/_xml.py Normal file
View File

@@ -0,0 +1,99 @@
#---------------------------------------------------------------------------
# Name: etg/_xml.py
# Author: Robin Dunn
#
# Created: 28-Nov-2012
# Copyright: (c) 2012 by Total Control Software
# License: wxWindows License
#---------------------------------------------------------------------------
import etgtools
import etgtools.tweaker_tools as tools
from etgtools import PyFunctionDef, PyCodeDef, PyPropertyDef
PACKAGE = "wx"
MODULE = "_xml"
NAME = "_xml" # Base name of the file to generate to for this script
DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [ 'wxXmlNode',
'wxXmlAttribute',
'wxXmlDocument',
]
# The list of other ETG scripts and back-end generator modules that are
# included as part of this module. These should all be items that are put in
# the wxWidgets "xml" library in a multi-lib build.
INCLUDES = [ ]
# Separate the list into those that are generated from ETG scripts and the
# rest. These lists can be used from the build scripts to get a list of
# sources and/or additional dependencies when building this extension module.
ETGFILES = ['etg/%s.py' % NAME] + tools.getEtgFiles(INCLUDES)
DEPENDS = tools.getNonEtgFiles(INCLUDES)
OTHERDEPS = [ ]
#---------------------------------------------------------------------------
def run():
# Parse the XML file(s) building a collection of Extractor objects
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
etgtools.parseDoxyXML(module, ITEMS)
module.check4unittest = False
#-----------------------------------------------------------------
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
module.addHeaderCode('#include <wxpy_api.h>')
module.addImport('_core')
module.addPyCode('import wx', order=10)
module.addInclude(INCLUDES)
#-----------------------------------------------------------------
module.addPyCode("""\
XmlProperty = wx.deprecated(XmlAttribute, 'Use XmlProperty instead.')
""")
c = module.find('wxXmlNode')
assert isinstance(c, etgtools.ClassDef)
c.find('wxXmlNode.parent').transferThis = True
c.find('AddAttribute.attr').transfer = True
c.find('AddChild.child').transfer = True
c.find('InsertChild.child').transfer = True
c.find('InsertChildAfter.child').transfer = True
c.find('RemoveChild.child').transferBack = True
# we like the other overload better
c.find('GetAttribute').findOverload('value').ignore()
c.find('SetAttributes').ignore()
c.find('SetChildren').ignore()
c = module.find('wxXmlDocument')
c.find('GetEncoding').ignore()
c.find('SetEncoding').ignore()
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)
#---------------------------------------------------------------------------
if __name__ == '__main__':
run()

160
etg/_xrc.py Normal file
View File

@@ -0,0 +1,160 @@
#---------------------------------------------------------------------------
# Name: etg/_xrc.py
# Author: Robin Dunn
#
# Created: 28-Nov-2012
# Copyright: (c) 2012 by Total Control Software
# License: wxWindows License
#---------------------------------------------------------------------------
import etgtools
import etgtools.tweaker_tools as tools
from etgtools import PyFunctionDef, PyCodeDef, PyPropertyDef
PACKAGE = "wx"
MODULE = "_xrc"
NAME = "_xrc" # Base name of the file to generate to for this script
DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [ 'wxXmlResource',
'wxXmlResourceHandler',
]
# The list of other ETG scripts and back-end generator modules that are
# included as part of this module. These should all be items that are put in
# the wxWidgets "xrc" library in a multi-lib build.
INCLUDES = [ ]
# Separate the list into those that are generated from ETG scripts and the
# rest. These lists can be used from the build scripts to get a list of
# sources and/or additional dependencies when building this extension module.
ETGFILES = ['etg/%s.py' % NAME] + tools.getEtgFiles(INCLUDES)
DEPENDS = tools.getNonEtgFiles(INCLUDES)
OTHERDEPS = [ ]
#---------------------------------------------------------------------------
def run():
# Parse the XML file(s) building a collection of Extractor objects
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
etgtools.parseDoxyXML(module, ITEMS)
module.check4unittest = False
#-----------------------------------------------------------------
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
module.addHeaderCode('#include <wxpy_api.h>')
module.addImport('_core')
module.addImport('_xml')
module.addPyCode('import wx', order=10)
module.addInclude(INCLUDES)
module.addInitializerCode("""\
wxXmlInitResourceModule();
""")
module.addHeaderCode('#include <wx/xrc/xmlres.h>')
module.addHeaderCode('#include <wx/fs_mem.h>')
module.addHeaderCode('#include "wxpybuffer.h"')
#-----------------------------------------------------------------
c = module.find('wxXmlResource')
assert isinstance(c, etgtools.ClassDef)
c.addPrivateCopyCtor()
# Add a bit of code to the ctors to call InitAllHandlers(), for
# compatibility with Classic
for ctor in c.find('wxXmlResource').all():
template = """\
Py_BEGIN_ALLOW_THREADS
sipCpp = new sipwxXmlResource({args});
sipCpp->InitAllHandlers();
Py_END_ALLOW_THREADS
"""
if 'filemask' in ctor.argsString:
args = '*filemask,flags,*domain'
else:
args = 'flags,*domain'
ctor.setCppCode_sip(template.format(args=args))
c.addPublic()
c.addCppMethod('bool', 'LoadFromString', '(wxPyBuffer* data)',
doc="Load the resource from a string or other data buffer compatible object.",
#protection='public',
body="""\
static int s_memFileIdx = 0;
// Check for memory FS. If not present, load the handler:
wxMemoryFSHandler::AddFile(wxT("XRC_resource/dummy_file"),
wxT("dummy data"));
wxFileSystem fsys;
wxFSFile *f = fsys.OpenFile(wxT("memory:XRC_resource/dummy_file"));
wxMemoryFSHandler::RemoveFile(wxT("XRC_resource/dummy_file"));
if (f)
delete f;
else
wxFileSystem::AddHandler(new wxMemoryFSHandler);
// Now put the resource data into the memory FS
wxString filename(wxT("XRC_resource/data_string_"));
filename << s_memFileIdx;
s_memFileIdx += 1;
wxMemoryFSHandler::AddFile(filename, data->m_ptr, data->m_len);
// Load the "file" into the resource object
bool retval = self->Load(wxT("memory:") + filename );
return retval;
""")
c.find('AddHandler.handler').transfer = True
c.find('InsertHandler').transfer = True
c.find('Set.res').transfer = True
c.find('Set').transferBack = True
module.addPyFunction('EmptyXmlResource', '(flags=XRC_USE_LOCALE, domain="")',
deprecated="Use XmlResource instead",
doc='A compatibility wrapper for the XmlResource(flags, domain) constructor',
body='return XmlResource(flags, domain)')
module.addPyFunction('XRCID', '(str_id, value_if_not_found=wx.ID_NONE)',
doc='Returns a numeric ID that is equivalent to the string ID used in an XML resource.',
body='return XmlResource.GetXRCID(str_id, value_if_not_found)')
module.addPyFunction('XRCCTRL', '(window, str_id, *ignoreargs)',
doc='Returns the child window associated with the string ID in an XML resource.',
body='return window.FindWindowById(XRCID(str_id))')
module.addItem(etgtools.WigCode("""\
class wxXmlSubclassFactory
{
public:
wxXmlSubclassFactory();
virtual ~wxXmlSubclassFactory();
virtual wxObject *Create(const wxString& className) = 0;
};"""))
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)
#---------------------------------------------------------------------------
if __name__ == '__main__':
run()

View File

@@ -244,6 +244,37 @@ extensions.append(ext)
cfg.CLEANUP.append(opj(cfg.PKGDIR, 'html2.py'))
etg = loadETG('etg/_xml.py')
tgDepends = etg.DEPENDS + etg.OTHERDEPS
ext = Extension('_xml', getEtgSipCppFiles(etg),
depends = getEtgSipHeaders(etg),
include_dirs = cfg.includes,
define_macros = cfg.defines,
library_dirs = cfg.libdirs,
libraries = cfg.libs + cfg.makeLibName('xml', True),
extra_compile_args = cfg.cflags,
extra_link_args = cfg.lflags,
)
extensions.append(ext)
cfg.CLEANUP.append(opj(cfg.PKGDIR, 'xml.py'))
etg = loadETG('etg/_xrc.py')
tgDepends = etg.DEPENDS + etg.OTHERDEPS
ext = Extension('_xrc', getEtgSipCppFiles(etg),
depends = getEtgSipHeaders(etg),
include_dirs = cfg.includes,
define_macros = cfg.defines,
library_dirs = cfg.libdirs,
libraries = cfg.libs + cfg.makeLibName('xml', True) + cfg.makeLibName('xrc', True),
extra_compile_args = cfg.cflags,
extra_link_args = cfg.lflags,
)
extensions.append(ext)
cfg.CLEANUP.append(opj(cfg.PKGDIR, 'xrc.py'))
#----------------------------------------------------------------------

View File

@@ -34,8 +34,8 @@ class PIImportTest(unittest.TestCase):
def test_core_pi(self):
self.runPI('core.pi')
#def test_adv_pi(self):
# self.runPI('adv.pi')
def test_adv_pi(self):
self.runPI('adv.pi')
def test_stc_pi(self):
self.runPI('stc.pi')
@@ -48,6 +48,14 @@ class PIImportTest(unittest.TestCase):
def test_dataview_pi(self):
self.runPI('dataview.pi')
def test_xml_pi(self):
self.runPI('xml.pi')
def test_xrc_pi(self):
self.runPI('xrc.pi')
#---------------------------------------------------------------------------

43
unittests/test_xml.py Normal file
View File

@@ -0,0 +1,43 @@
import imp_unittest, unittest
import wtc
import wx
import wx.xml as xml
#---------------------------------------------------------------------------
class xml_Tests(wtc.WidgetTestCase):
def test_xml1(self):
doc = xml.XmlDocument()
node = xml.XmlNode(xml.XML_DOCUMENT_NODE, 'document')
doc.SetDocumentNode(node)
root = xml.XmlNode(xml.XML_ELEMENT_NODE, 'root')
node.AddChild(root)
root.AddAttribute('name1', 'value1')
root.AddAttribute(xml.XmlAttribute('name2', 'value2'))
def test_xml2(self):
xml.XML_ELEMENT_NODE
xml.XML_ATTRIBUTE_NODE
xml.XML_TEXT_NODE
xml.XML_CDATA_SECTION_NODE
xml.XML_ENTITY_REF_NODE
xml.XML_ENTITY_NODE
xml.XML_PI_NODE
xml.XML_COMMENT_NODE
xml.XML_DOCUMENT_NODE
xml.XML_DOCUMENT_TYPE_NODE
xml.XML_DOCUMENT_FRAG_NODE
xml.XML_NOTATION_NODE
xml.XML_HTML_DOCUMENT_NODE
xml.XML_NO_INDENTATION
xml.XMLDOC_NONE
xml.XMLDOC_KEEP_WHITESPACE_NODES
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()

60
unittests/test_xrc.py Normal file
View File

@@ -0,0 +1,60 @@
import imp_unittest, unittest
import wtc
import wx
import wx.xrc as xrc
import os
xrcFile = os.path.join(os.path.dirname(__file__), 'xrctest.xrc')
#---------------------------------------------------------------------------
class xrc_Tests(wtc.WidgetTestCase):
def checkXmlRes(self, xmlres):
assert isinstance(xmlres, xrc.XmlResource)
#xmlres.InitAllHandlers()
f = xmlres.LoadFrame(self.frame, 'MainFrame')
self.assertNotEqual(f, None)
f.Show()
self.myYield()
id = xrc.XRCID('MainPanel')
self.assertTrue(id != -1)
self.assertTrue(isinstance(id, int))
ctrl = xrc.XRCCTRL(f, 'TitleText')
self.assertTrue(ctrl != None)
self.assertTrue(isinstance(ctrl, wx.StaticText))
def test_xrc1(self):
xmlres = xrc.XmlResource(xrcFile)
self.checkXmlRes(xmlres)
def test_xrc2(self):
xmlres = xrc.XmlResource()
xmlres.LoadFile(xrcFile)
self.checkXmlRes(xmlres)
def test_xrc3(self):
xmlres = xrc.XmlResource()
text = open(xrcFile).read()
xmlres.LoadFromString(text)
self.checkXmlRes(xmlres)
def test_xrc4(self):
xmlres = xrc.XmlResource(xrcFile)
p = xmlres.LoadObjectRecursively(self.frame, 'MainPanel', 'wxPanel')
self.assertNotEqual(p, None)
self.frame.SendSizeEvent()
self.myYield()
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()

66
unittests/xrctest.xrc Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" ?>
<resource>
<object class="wxFrame" name="MainFrame">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxPanel" name="MainPanel">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxStaticText" name="TitleText">
<label>Hello World</label>
<fg>#3056D3</fg>
<font>
<size>24</size>
<style>normal</style>
<weight>bold</weight>
<underlined>0</underlined>
<family>swiss</family>
</font>
</object>
<flag>wxALL|wxEXPAND|wxALIGN_CENTRE</flag>
<border>10</border>
</object>
<object class="sizeritem">
<object class="wxStaticLine"/>
<flag>wxEXPAND</flag>
</object>
<object class="sizeritem">
<object class="wxFlexGridSizer">
<object class="sizeritem">
<object class="wxStaticText">
<label>Howdy!</label>
</object>
<flag>wxALIGN_CENTRE_VERTICAL</flag>
</object>
<object class="sizeritem">
<object class="wxTextCtrl"/>
<flag>wxEXPAND</flag>
</object>
<object class="spacer">
<size>5,5</size>
</object>
<object class="sizeritem">
<object class="wxButton" name="wxID_OK"/>
</object>
<cols>2</cols>
<rows>0</rows>
<vgap>5</vgap>
<hgap>5</hgap>
<growablecols>1</growablecols>
</object>
<option>1</option>
<flag>wxALL|wxEXPAND</flag>
<border>10</border>
</object>
</object>
</object>
<option>1</option>
<flag>wxEXPAND</flag>
</object>
</object>
<size>400,250</size>
<title>This is a test</title>
</object>
</resource>

69
wscript
View File

@@ -93,6 +93,13 @@ def configure(conf):
_copyEnvGroup(conf.env, '_WX', '_WXWEBVIEW')
conf.env.LIB_WXWEBVIEW += cfg.makeLibName('webview')
_copyEnvGroup(conf.env, '_WX', '_WXXML')
conf.env.LIB_WXWEBVIEW += cfg.makeLibName('xml')
_copyEnvGroup(conf.env, '_WX', '_WXXRC')
conf.env.LIB_WXWEBVIEW += cfg.makeLibName('xrc')
# tweak the PYEXT compile and link flags if making a --debug build
@@ -148,6 +155,15 @@ def configure(conf):
args='--cxxflags --libs webview,core,net',
uselib_store='WXWEBVIEW', mandatory=True)
conf.check_cfg(path=conf.options.wx_config, package='',
args='--cxxflags --libs xml,core,net',
uselib_store='WXXML', mandatory=True)
conf.check_cfg(path=conf.options.wx_config, package='',
args='--cxxflags --libs xrc,xml,core,net',
uselib_store='WXXRC', mandatory=True)
# NOTE: This assumes that if the platform is not win32 (from
# the test above) and not darwin then we must be using the
@@ -261,75 +277,86 @@ def build(bld):
etg = loadETG('etg/_core.py')
rc = ['src/wxc.rc'] if isWindows else []
core = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_core'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WX WXPY',
)
)
makeExtCopyRule(bld, '_core')
etg = loadETG('etg/_adv.py')
adv = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_adv'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXADV WXPY',
)
)
makeExtCopyRule(bld, '_adv')
etg = loadETG('etg/_dataview.py')
dataview = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_dataview'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXADV WXPY', # dataview classes are also in the adv library
)
)
makeExtCopyRule(bld, '_dataview')
etg = loadETG('etg/_stc.py')
stc = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_stc'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXSTC WXPY',
)
)
makeExtCopyRule(bld, '_stc')
etg = loadETG('etg/_html.py')
adv = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_html'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXHTML WXPY',
)
)
makeExtCopyRule(bld, '_html')
etg = loadETG('etg/_glcanvas.py')
adv = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_glcanvas'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXGL WXPY',
)
)
makeExtCopyRule(bld, '_glcanvas')
etg = loadETG('etg/_html2.py')
adv = bld(
features = 'c cxx cxxshlib pyext',
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_html2'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXWEBVIEW WXPY',
)
)
makeExtCopyRule(bld, '_html2')
etg = loadETG('etg/_xml.py')
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_xml'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXXML WXPY',
)
makeExtCopyRule(bld, '_xml')
etg = loadETG('etg/_xrc.py')
bld(features = 'c cxx cxxshlib pyext',
target = makeTargetName(bld, '_xrc'),
source = getEtgSipCppFiles(etg) + rc,
uselib = 'WXXRC WXPY',
)
makeExtCopyRule(bld, '_xrc')
#-----------------------------------------------------------------------------
# helpers