New preview build, and missing file
Robin Dunn
robin at alldunn.com
Mon Nov 26 18:57:55 PST 2007
Hi all,
There is a new preview build that is almost finished building, but I =
just realized that I never updated pywxrc.py from the SOC2007_XRCED =
branch when I merged Roman's work over to the main tree. Since the =
build is almost finished I won't worry about doing it all over again and =
will just attach the file here. If you want to test pywxrc.py or the =
code generation in XRCed please copy this file to your wx/tools folder.
-- =
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!
-------------- next part --------------
#----------------------------------------------------------------------
# Name: wx.tools.pywxrc
# Purpose: XML resource compiler
#
# Author: Robin Dunn
# Based on wxrc.cpp by Vaclav Slavik, Eduardo Marques
# Ported to Python in order to not require yet another
# binary in wxPython distributions
#
# Massive rework by Eli Golovinsky
#
# Editable blocks by Roman Rolinsky
#
# RCS-ID: $Id$
# Copyright: (c) 2004 by Total Control Software, 2000 Vaclav Slavik
# Licence: wxWindows license
#----------------------------------------------------------------------
"""
pywxrc -- Python XML resource compiler
(see http://wiki.wxpython.org/index.cgi/pywxrc for more info)
Usage: python pywxrc.py -h
python pywxrc.py [-p] [-g] [-e] [-o filename] xrc input files... =
=
-h, --help show help message
-p, --python generate python module
-g, --gettext output list of translatable strings (may be combined with =
-p)
-e, --embed embed XRC resources in the output file
-o, --output output filename, or - for stdout
"""
import sys, os, getopt, glob, re, cPickle
import xml.dom.minidom as minidom
import wx
import wx.xrc
#----------------------------------------------------------------------
reBeginBlock =3D re.compile(r'^#!XRCED:begin-block:(\S+)')
reEndBlock =3D re.compile(r'^#!XRCED:end-block:(\S+)')
class PythonTemplates:
FILE_HEADER =3D """\
# This file was automatically generated by pywxrc.
# -*- coding: UTF-8 -*-
import wx
import wx.xrc as xrc
__res =3D None
def get_resources():
\"\"\" This function provides access to the XML resources in this modul=
e.\"\"\"
global __res
if __res =3D=3D None:
__init_resources()
return __res
"""
CLASS_HEADER =3D """\
class xrc%(windowName)s(wx.%(windowClass)s):
#!XRCED:begin-block:xrc%(windowName)s.PreCreate
def PreCreate(self, pre):
\"\"\" This function is called during the class's initialization.
=
Override it for custom setup before the window is created usually to
set additional window styles using SetWindowStyle() and SetExtraSty=
le().
\"\"\"
pass
=
#!XRCED:end-block:xrc%(windowName)s.PreCreate
def __init__(self, parent):
# Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoSta=
geCreation)
pre =3D wx.Pre%(windowClass)s()
self.PreCreate(pre)
get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
self.PostCreate(pre)
# Define variables for the controls, bind event handlers
"""
SUBCLASS_HEADER =3D """\
class %(subclass)s(wx.%(windowClass)s):
def __init__(self):
# Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoSta=
geCreation)
pre =3D wx.Pre%(windowClass)s()
self.PostCreate(pre)
self.Bind(wx.EVT_WINDOW_CREATE, self.OnCreate)
#!XRCED:begin-block:%(subclass)s._PostInit
def _PostInit(self):
\"\"\" This function is called after the subclassed object is creat=
ed.
=
Override it for custom setup before the window is created usually to
set additional window styles using SetWindowStyle() and SetExtraSty=
le().
\"\"\"
pass
#!XRCED:end-block:%(subclass)s._PostInit
=
def OnCreate(self, evt):
self.Unbind(wx.EVT_WINDOW_CREATE)
self._PostInit()
"""
CREATE_WIDGET_VAR =3D """\
self.%(widgetName)s =3D xrc.XRCCTRL(self, \"%(widgetName)s\")
"""
BIND_WIDGET_EVENT =3D """\
self.Bind(wx.%(event)s, self.%(eventHandler)s, %(eventObject)s)
"""
BIND_EVENT =3D """\
self.Bind(wx.%(event)s, self.%(eventHandler)s)
"""
CREATE_EVENT_HANDLER =3D """\
#!XRCED:begin-block:xrc%(windowName)s.%(eventHandler)s
def %(eventHandler)s(self, evt):
# Replace with event handler code
print \"%(eventHandler)s()\"
#!XRCED:end-block:xrc%(windowName)s.%(eventHandler)s =
"""
MENU_CLASS_HEADER =3D """\
class xrc%(windowName)s(wx.%(windowClass)s):
def __init__(self):
pre =3D get_resources().LoadMenu("%(windowName)s")
# This is a copy of Robin's PostCreate voodoo magic in wx.Window th=
at
# relinks the self object with the menu object.
self.this =3D pre.this
self.thisown =3D pre.thisown
pre.thisown =3D 0
if hasattr(self, '_setOORInfo'):
self._setOORInfo(self)
# Define variables for the menu items
"""
MENUBAR_CLASS_HEADER =3D """\
class xrc%(windowName)s(wx.%(windowClass)s):
def __init__(self):
pre =3D get_resources().LoadMenuBar("%(windowName)s")
self.PostCreate(pre)
=
# Define variables for the menu items
"""
CREATE_MENUITEM_VAR =3D """\
self.%(widgetName)s =3D %(menuObject)s.FindItemById(xrc.XRCID(\"%(w=
idgetName)s\"))
"""
INIT_RESOURE_HEADER =3D """\
# ------------------------ Resource data ----------------------
def __init_resources():
global __res
__res =3D xrc.EmptyXmlResource()
"""
LOAD_RES_FILE =3D """\
__res.Load('%(resourceFilename)s')"""
FILE_AS_STRING =3D """\
%(filename)s =3D '''\\
%(fileData)s'''
"""
PREPARE_MEMFS =3D """\
wx.FileSystem.AddHandler(wx.MemoryFSHandler())
"""
ADD_FILE_TO_MEMFS =3D """\
wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filenam=
e)s)
"""
LOAD_RES_MEMFS =3D """\
__res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
"""
GETTEXT_DUMMY_FUNC =3D """
# ----------------------- Gettext strings ---------------------
def __gettext_strings():
# This is a dummy function that lists all the strings that are used in
# the XRC file in the _("a string") format to be recognized by GNU
# gettext utilities (specificaly the xgettext utility) and the
# mki18n.py script. For more information see:
# http://wiki.wxpython.org/index.cgi/Internationalization =
=
def _(str): pass
=
%s
"""
#----------------------------------------------------------------------
class XmlResourceCompiler:
=
templates =3D PythonTemplates()
"""This class generates Python code from XML resource files (XRC)."""
def MakePythonModule(self, inputFiles, outputFilename,
embedResources=3DFalse, generateGetText=3DFalse):
self.blocks =3D {}
self.outputFilename =3D outputFilename
outputFile =3D self._OpenOutputFile(outputFilename)
classes =3D []
subclasses =3D []
resources =3D []
gettextStrings =3D []
# process all the inputFiles, collecting the output data
for inFile in inputFiles:
resourceDocument =3D minidom.parse(inFile)
subclasses.append(self.GenerateSubclasses(resourceDocument))
classes.append(self.GenerateClasses(resourceDocument))
if embedResources:
res =3D self.GenerateInitResourcesEmbedded(inFile, resource=
Document)
else:
res =3D self.GenerateInitResourcesFile(inFile, resourceDocu=
ment)
resources.append(res)
if generateGetText:
gettextStrings +=3D self.FindStringsInNode(resourceDocument=
.firstChild)
=
# now write it all out
print >>outputFile, self.templates.FILE_HEADER
# Note: Technically it is not legal to have anything other
# than ascii for class and variable names, but since the user
# can create the XML with non-ascii names we'll go ahead and
# allow for it here, and then let Python complain about it
# later when they try to run the program.
if subclasses:
subclasses =3D self.ReplaceBlocks(u"\n".join(subclasses))
print >>outputFile, subclasses.encode("UTF-8")
if classes:
classes =3D self.ReplaceBlocks(u"\n".join(classes))
print >>outputFile, classes.encode("UTF-8")
print >>outputFile, self.templates.INIT_RESOURE_HEADER
if embedResources:
print >>outputFile, self.templates.PREPARE_MEMFS
resources =3D u"\n".join(resources)
print >>outputFile, resources.encode("UTF-8")
if generateGetText:
# These have already been converted to utf-8...
gettextStrings =3D [' _("%s")' % s for s in gettextStrings]
gettextStrings =3D "\n".join(gettextStrings)
print >>outputFile, self.templates.GETTEXT_DUMMY_FUNC % gettext=
Strings
#-------------------------------------------------------------------
def MakeGetTextOutput(self, inputFiles, outputFilename):
"""
Just output the gettext strings by themselves, with no other
code generation.
"""
outputFile =3D self._OpenOutputFile(outputFilename)
for inFile in inputFiles:
resourceDocument =3D minidom.parse(inFile)
resource =3D resourceDocument.firstChild
strings =3D self.FindStringsInNode(resource)
strings =3D ['_("%s");' % s for s in strings]
print >>outputFile, "\n".join(strings)
#-------------------------------------------------------------------
def GenerateClasses(self, resourceDocument):
outputList =3D []
=
resource =3D resourceDocument.firstChild
topWindows =3D [e for e in resource.childNodes
if e.nodeType =3D=3D e.ELEMENT_NODE and e.tagName =3D=
=3D "object" \
and not e.getAttribute('subclass')]
=
# Generate a class for each top-window object (Frame, Panel, Dialog=
, etc.)
for topWindow in topWindows:
windowClass =3D topWindow.getAttribute("class")
windowClass =3D re.sub("^wx", "", windowClass)
windowName =3D topWindow.getAttribute("name")
=
if windowClass in ["MenuBar"]:
outputList.append(self.templates.MENUBAR_CLASS_HEADER % loc=
als())
elif windowClass in ["Menu"]:
outputList.append(self.templates.MENU_CLASS_HEADER % locals=
())
else:
outputList.append(self.templates.CLASS_HEADER % locals())
# Generate a variable for each control, and standard event hand=
lers
# for standard controls.
vars =3D []
for widget in topWindow.getElementsByTagName("object"):
assign_var =3D False
for node in widget.getElementsByTagName("XRCED"):
if node.parentNode is widget:
try:
elem =3D node.getElementsByTagName("assign_var"=
)[0]
except IndexError:
continue
if elem.childNodes:
ch =3D elem.childNodes[0]
if ch.nodeType =3D=3D ch.TEXT_NODE and bool(ch.=
nodeValue):
assign_var =3D True
if not assign_var: continue
widgetClass =3D widget.getAttribute("class")
widgetClass =3D re.sub("^wx", "", widgetClass)
widgetName =3D widget.getAttribute("name")
if not widgetName: continue
vars.append(widgetName)
if (widgetName !=3D "" and widgetClass !=3D ""):
if widgetClass =3D=3D "MenuItem":
if windowClass =3D=3D "MenuBar":
menuObject =3D 'self'
else:
menuObject =3D 'self.GetMenuBar()'
outputList.append(self.templates.CREATE_MENUITEM_VA=
R % locals())
elif widgetClass not in \
['tool', 'unknown', 'notebookpage', 'separator', 's=
izeritem']:
outputList.append(self.templates.CREATE_WIDGET_VAR =
% locals())
outputList.append('\n')
# Generate child event handlers
eventHandlers =3D [] =
for elem in topWindow.getElementsByTagName("XRCED"):
try:
eventNode =3D elem.getElementsByTagName("events")[0]
except IndexError:
continue
events =3D eventNode.childNodes[0].data.split('|')
for event in events:
if elem.parentNode is topWindow:
eventHandler =3D "On%s" % event[4:].capitalize()
outputList.append(self.templates.BIND_EVENT % local=
s())
eventHandlers.append(eventHandler)
else:
widget =3D elem.parentNode
widgetClass =3D widget.getAttribute("class")
widgetClass =3D re.sub("^wx", "", widgetClass)
widgetName =3D widget.getAttribute("name")
if not widgetName or not widgetClass: continue
eventObject =3D None
if widgetClass =3D=3D "MenuItem" and windowClass !=
=3D "MenuBar":
if widgetName[:2] =3D=3D "wx":
eventObject =3D 'id=3Dwx.%s' % re.sub("^wx"=
, "", widgetName)
eventHandler =3D "On%s_%s" % (event[4:].capital=
ize(), widgetName)
if widgetName in vars: eventObject =3D "self.%s=
" % widgetName
else:
eventHandler =3D "On%s_%s" % (event[4:].capital=
ize(), widgetName)
if widgetName in vars: eventObject =3D "self.%s=
" % widgetName
if not eventObject:
eventObject =3D "id=3Dxrc.XRCID('%s')" % widget=
Name
outputList.append(self.templates.BIND_WIDGET_EVENT =
% locals())
eventHandlers.append(eventHandler)
outputList.append("\n")
for eventHandler in eventHandlers:
block =3D "xrc%(windowName)s.%(eventHandler)s" % locals()
try:
outputList.append(self.blocks[block])
except KeyError:
outputList.append(self.templates.CREATE_EVENT_HANDLER %=
locals())
outputList.append("\n")
outputList.append("\n")
=
return "".join(outputList)
#-------------------------------------------------------------------
def GenerateSubclasses(self, resourceDocument):
outputList =3D []
=
objectNodes =3D resourceDocument.getElementsByTagName("object")
subclasses =3D set()
bases =3D {}
baseName =3D os.path.splitext(self.outputFilename)[0]
print baseName
for node in objectNodes:
subclass =3D node.getAttribute('subclass')
if subclass:
module =3D subclass[:subclass.find('.')]
print module,subclass
if module !=3D baseName: continue
klass =3D node.getAttribute("class")
if subclass not in subclasses:
subclasses.add(subclass)
bases[subclass] =3D klass
else:
if klass !=3D bases[subclass]:
print 'pywxrc: error: conflicting base classes for =
subclass %(subclass)s' \
% subclass
=
# Generate subclasses
for subclass in subclasses:
windowClass =3D bases[subclass]
subclass =3D re.sub("^\S+\.", "", subclass)
windowClass =3D re.sub("^wx", "", windowClass)
outputList.append(self.templates.SUBCLASS_HEADER % locals())
outputList.append('\n')
=
return "".join(outputList)
#-------------------------------------------------------------------
def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocum=
ent):
outputList =3D []
files =3D []
resourcePath =3D os.path.split(resourceFilename)[0]
memoryPath =3D self.GetMemoryFilename(os.path.splitext(os.path.spli=
t(resourceFilename)[1])[0])
resourceFilename =3D self.GetMemoryFilename(os.path.split(resourceF=
ilename)[1])
=
self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, reso=
urcePath)
=
filename =3D resourceFilename
fileData =3D resourceDocument.toxml() # what about this? encoding=
=3DresourceDocument.encoding)
outputList.append(self.templates.FILE_AS_STRING % locals())
for f in files:
filename =3D self.GetMemoryFilename(f)
fileData =3D self.FileToString(os.path.join(resourcePath, f))
outputList.append(self.templates.FILE_AS_STRING % locals())
for f in [resourceFilename] + files:
filename =3D self.GetMemoryFilename(f)
outputList.append(self.templates.ADD_FILE_TO_MEMFS % locals())
=
outputList.append(self.templates.LOAD_RES_MEMFS % locals())
=
return "".join(outputList)
=
#-------------------------------------------------------------------
def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
# take only the filename portion out of resourceFilename
resourceFilename =3D os.path.split(resourceFilename)[1]
outputList =3D []
outputList.append(self.templates.LOAD_RES_FILE % locals())
return "".join(outputList)
#-------------------------------------------------------------------
def GetMemoryFilename(self, filename):
# Remove special chars from the filename
return re.sub(r"[^A-Za-z0-9_]", "_", filename)
#-------------------------------------------------------------------
def FileToString(self, filename):
outputList =3D []
=
buffer =3D open(filename, "rb").read()
fileLen =3D len(buffer)
linelng =3D 0
for i in xrange(fileLen):
s =3D buffer[i]
c =3D ord(s)
if s =3D=3D '\n':
tmp =3D s
linelng =3D 0
elif c < 32 or c > 127 or s =3D=3D "'":
tmp =3D "\\x%02x" % c
elif s =3D=3D "\\":
tmp =3D "\\\\" =
else:
tmp =3D s
if linelng > 70:
linelng =3D 0
outputList.append("\\\n")
=
outputList.append(tmp)
linelng +=3D len(tmp)
=
return "".join(outputList)
=
#-------------------------------------------------------------------
def NodeContainsFilename(self, node):
""" Does 'node' contain filename information at all? """
# Any bitmaps:
if node.nodeName =3D=3D "bitmap":
return True
if node.nodeName =3D=3D "icon":
return True
# URLs in wxHtmlWindow:
if node.nodeName =3D=3D "url":
return True
# wxBitmapButton:
parent =3D node.parentNode
if parent.__class__ !=3D minidom.Document and \
parent.getAttribute("class") =3D=3D "wxBitmapButton" and \
(node.nodeName =3D=3D "focus" or node.nodeName =3D=3D "disabled"=
or
node.nodeName =3D=3D "selected"):
return True
# wxBitmap or wxIcon toplevel resources:
if node.nodeName =3D=3D "object":
klass =3D node.getAttribute("class")
if klass =3D=3D "wxBitmap" or klass =3D=3D "wxIcon":
return True
return False
#-------------------------------------------------------------------
def ReplaceFilenamesInXRC(self, node, files, resourcePath):
""" Finds all files mentioned in resource file, e.g. <bitmap>filena=
me</bitmap> =
and replaces them with the memory filenames.
=
Fills a list of the filenames found."""
=
# Is 'node' XML node element?
if node is None: return
if node.nodeType !=3D minidom.Document.ELEMENT_NODE: return
containsFilename =3D self.NodeContainsFilename(node);
for n in node.childNodes:
if (containsFilename and
(n.nodeType =3D=3D minidom.Document.TEXT_NODE or
n.nodeType =3D=3D minidom.Document.CDATA_SECTION_NODE)):
=
filename =3D n.nodeValue
memoryFilename =3D self.GetMemoryFilename(filename)
n.nodeValue =3D memoryFilename
if filename not in files:
files.append(filename)
# Recurse into children
if n.nodeType =3D=3D minidom.Document.ELEMENT_NODE:
self.ReplaceFilenamesInXRC(n, files, resourcePath);
#-------------------------------------------------------------------
def FindStringsInNode(self, parent):
def is_number(st):
try:
i =3D int(st)
return True
except ValueError:
return False
=
strings =3D []
if parent is None:
return strings;
for child in parent.childNodes:
if ((parent.nodeType =3D=3D parent.ELEMENT_NODE) and
# parent is an element, i.e. has subnodes...
(child.nodeType =3D=3D child.TEXT_NODE or
child.nodeType =3D=3D child.CDATA_SECTION_NODE) and
# ...it is textnode...
(
parent.tagName =3D=3D "label" or
(parent.tagName =3D=3D "value" and
not is_number(child.nodeValue)) or
parent.tagName =3D=3D "help" or
parent.tagName =3D=3D "longhelp" or
parent.tagName =3D=3D "tooltip" or
parent.tagName =3D=3D "htmlcode" or
parent.tagName =3D=3D "title" or
parent.tagName =3D=3D "item"
)):
# ...and known to contain translatable string
if (parent.getAttribute("translate") !=3D "0"):
strings.append(self.ConvertText(child.nodeValue))
# subnodes:
if child.nodeType =3D=3D child.ELEMENT_NODE:
strings +=3D self.FindStringsInNode(child)
return strings
#-------------------------------------------------------------------
def ConvertText(self, st):
st2 =3D ""
dt =3D list(st)
skipNext =3D False
for i in range(len(dt)):
if skipNext:
skipNext =3D False
continue
=
if dt[i] =3D=3D '_':
if dt[i+1] =3D=3D '_':
st2 +=3D '_'
skipNext =3D True
else:
st2 +=3D '&'
elif dt[i] =3D=3D '\n':
st2 +=3D '\\n'
elif dt[i] =3D=3D '\t':
st2 +=3D '\\t'
elif dt[i] =3D=3D '\r':
st2 +=3D '\\r'
elif dt[i] =3D=3D '\\':
if dt[i+1] not in ['n', 't', 'r']:
st2 +=3D '\\\\'
else:
st2 +=3D '\\'
elif dt[i] =3D=3D '"':
st2 +=3D '\\"'
else: =
st2 +=3D dt[i]
return st2.encode("UTF-8") =
#-------------------------------------------------------------------
# Replace editable block contents with previous
def ReplaceBlocks(self, input):
output =3D []
block =3D None
blockLines =3D []
for l in input.split('\n'):
if not block:
mo =3D reBeginBlock.match(l)
if mo and mo.groups()[0] in self.blocks:
block =3D mo.groups()[0]
output.append(self.blocks[block])
else:
output.append(l + '\n')
else:
mo =3D reEndBlock.match(l)
if mo:
if mo.groups()[0] !=3D block:
print "pywxrc: error: block mismatch: %s !=3D %s" %=
(block, mo.groups()[0])
block =3D None
return ''.join(output)
#-------------------------------------------------------------------
def _OpenOutputFile(self, outputFilename):
if outputFilename =3D=3D "-":
outputFile =3D sys.stdout
else:
# Parse existing file to collect editable blocks
if os.path.isfile(outputFilename):
outputFile =3D open(outputFilename)
block =3D None
blockLines =3D []
for l in outputFile.readlines():
if not block:
mo =3D reBeginBlock.match(l)
if mo:
block =3D mo.groups()[0]
blockLines =3D [l]
else:
blockLines.append(l)
mo =3D reEndBlock.match(l)
if mo:
if mo.groups()[0] !=3D block:
print "pywxrc: error: block mismatch: %s !=
=3D %s" % (block, mo.groups()[0])
self.blocks[block] =3D "".join(blockLines)
block =3D None
=
try:
outputFile =3D open(outputFilename, "wt")
except IOError:
raise IOError("Can't write output to '%s'" % outputFilename)
return outputFile
=
#---------------------------------------------------------------------------
def main(args):
resourceFilename =3D ""
outputFilename =3D None
embedResources =3D False
generateGetText =3D False
generatePython =3D False
try:
opts, args =3D getopt.gnu_getopt(args,
"hpgeo:",
"help python gettext embed output=3D=
".split())
except getopt.GetoptError, e:
print "\nError : %s\n" % str(e)
print __doc__
sys.exit(1)
# If there is no input file argument, show help and exit
if not args:
print __doc__
print "No xrc input file was specified."
sys.exit(1)
# Parse options and arguments
for opt, val in opts:
if opt in ["-h", "--help"]:
print __doc__
sys.exit(1)
if opt in ["-p", "--python"]:
generatePython =3D True
if opt in ["-o", "--output"]:
outputFilename =3D val
=
if opt in ["-e", "--embed"]:
embedResources =3D True
if opt in ["-g", "--gettext"]:
generateGetText =3D True
# check for and expand any wildcards in the list of input files
inputFiles =3D []
for arg in args:
inputFiles +=3D glob.glob(arg)
comp =3D XmlResourceCompiler()
=
try:
if generatePython:
if not outputFilename:
outputFilename =3D os.path.splitext(args[0])[0] + "_xrc.py"
comp.MakePythonModule(inputFiles, outputFilename,
embedResources, generateGetText)
elif generateGetText:
if not outputFilename:
outputFilename =3D '-'
comp.MakeGetTextOutput(inputFiles, outputFilename)
else:
print __doc__
print "One or both of -p, -g must be specified."
sys.exit(1)
=
=
except IOError, e:
print >>sys.stderr, "%s." % str(e)
else:
if outputFilename !=3D "-":
print >>sys.stderr, "Resources written to %s." % outputFilename
if __name__ =3D=3D "__main__":
main(sys.argv[1:])
More information about the wxpython-dev
mailing list