wxWidgets/wxPython/wx/tools/pywxrc.py
2006-05-03 01:47:57 +00:00

473 lines
15 KiB
Python

#----------------------------------------------------------------------
# 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
#
# 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 <resource.xrc> [-e] [-g] [-o filename]
-h, --help show help message
-e, --embed embed resources in the output file
-g, --gettext embed list of translatable strings in the output file
-o, --output output filename, or - for stdout
"""
import sys, os, getopt, glob, re
import xml.dom.minidom as minidom
import wx
import wx.xrc
#----------------------------------------------------------------------
class PythonTemplates:
FILE_HEADER = """\
# This file was automatically generated by pywxrc, do not edit by hand.
import wx
import wx.xrc as xrc
__res = None
def get_resources():
\"\"\" This function provides access to the XML resources in this module.\"\"\"
global __res
if __res == None:
__init_resources()
return __res
"""
CLASS_HEADER = """\
class xrc%(windowName)s(wx.%(windowClass)s):
def PreCreate(self):
\"\"\" 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 SetExtraStyle().\"\"\"
pass
def __init__(self, parent):
# Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
pre = wx.Pre%(windowClass)s()
get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
self.PreCreate()
self.PostCreate(pre)
# Define variables for the controls
"""
CREATE_WIDGET_VAR = """\
self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
"""
INIT_RESOURE_HEADER = """\
# ------------------------ Resource data ----------------------
def __init_resources():
"""
LOAD_RES_FILE = """\
global __res
__res = xrc.XmlResource('%(resourceFilename)s')
"""
FILE_AS_STRING = """\
%(filename)s = '''\\
%(fileData)s'''
"""
PREPARE_MEMFS = """\
# Load all the strings as memory files
wx.FileSystem.AddHandler(wx.MemoryFSHandler())
"""
ADD_FILE_TO_MEMFS = """\
wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
"""
LOAD_RES_MEMFS = """\
global __res
__res = xrc.EmptyXmlResource()
__res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
"""
GETTEXT_DUMMY_FUNC = """\
# ----------------------- 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
%(gettextStrings)s
"""
#----------------------------------------------------------------------
class XmlResourceCompiler:
templates = PythonTemplates()
"""This class generates Python code from XML resource files (XRC)."""
def MakePythonModule(self, resourceFilename, outputFilename,
embedResources=False, generateGetText=False):
if outputFilename == "-":
outputFile = sys.stdout
else:
try:
outputFile = open(outputFilename, "wt")
except IOError:
raise IOError("Can't write output to '%s'" % outputFilename)
resourceDocument = minidom.parse(resourceFilename)
print >>outputFile, self.templates.FILE_HEADER
print >>outputFile, self.GenerateClasses(resourceDocument)
if embedResources:
print >>outputFile, self.GenerateInitResourcesEmbedded(resourceFilename, resourceDocument)
else:
print >>outputFile, self.GenerateInitResourcesFile(resourceFilename, resourceDocument)
if generateGetText:
print >>outputFile, self.GenerateGetText(resourceDocument)
#-------------------------------------------------------------------
def GenerateClasses(self, resourceDocument):
outputList = []
resource = resourceDocument.firstChild
topWindows = [e for e in resource.childNodes
if e.nodeType == e.ELEMENT_NODE and e.tagName == "object"]
# Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
for topWindow in topWindows:
windowClass = topWindow.getAttribute("class")
windowClass = re.sub("^wx", "", windowClass)
windowName = topWindow.getAttribute("name")
outputList.append(self.templates.CLASS_HEADER % locals())
# Generate a variable for each control, and standard event handlers
# for standard controls.
for widget in topWindow.getElementsByTagName("object"):
widgetClass = widget.getAttribute("class")
widgetClass = re.sub("^wx", "", widgetClass)
widgetName = widget.getAttribute("name")
if (widgetName != "" and widgetClass != "" and
widgetClass not in
['tool', 'unknown', 'notebookpage',
'separator', 'sizeritem', 'MenuItem']):
outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
outputList.append('\n\n')
return "".join(outputList)
#-------------------------------------------------------------------
def GenerateGetText(self, resourceDocument):
resource = resourceDocument.firstChild
strings = self.FindStringsInNode(resource)
strings = [' _("%s")\n' % s for s in strings]
gettextStrings = "".join(strings)
return self.templates.GETTEXT_DUMMY_FUNC % locals()
#-------------------------------------------------------------------
def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
outputList = []
outputList.append(self.templates.INIT_RESOURE_HEADER)
files = []
resourcePath = os.path.split(resourceFilename)[0]
memoryPath = self.GetMemoryFilename(os.path.splitext(os.path.split(resourceFilename)[1])[0])
resourceFilename = self.GetMemoryFilename(os.path.split(resourceFilename)[1])
self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
filename = resourceFilename
fileData = resourceDocument.toxml()
outputList.append(self.templates.FILE_AS_STRING % locals())
for f in files:
filename = self.GetMemoryFilename(f)
fileData = self.FileToString(os.path.join(resourcePath, f))
outputList.append(self.templates.FILE_AS_STRING % locals())
outputList.append(self.templates.PREPARE_MEMFS % locals())
for f in [resourceFilename] + files:
filename = 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 = os.path.split(resourceFilename)[1]
outputList = []
outputList.append(self.templates.INIT_RESOURE_HEADER)
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 = []
buffer = open(filename, "rb").read()
fileLen = len(buffer)
linelng = 0
for i in xrange(fileLen):
s = buffer[i]
c = ord(s)
if s == '\n':
tmp = s
linelng = 0
elif c < 32 or c > 127 or s == "'":
tmp = "\\x%02x" % c
elif s == "\\":
tmp = "\\\\"
else:
tmp = s
if linelng > 70:
linelng = 0
outputList.append("\\\n")
outputList.append(tmp)
linelng += len(tmp)
return "".join(outputList)
#-------------------------------------------------------------------
def NodeContainsFilename(self, node):
""" Does 'node' contain filename information at all? """
# Any bitmaps:
if node.nodeName == "bitmap":
return True
if node.nodeName == "icon":
return True
# URLs in wxHtmlWindow:
if node.nodeName == "url":
return True
# wxBitmapButton:
parent = node.parentNode
if parent.__class__ != minidom.Document and \
parent.getAttribute("class") == "wxBitmapButton" and \
(node.nodeName == "focus" or node.nodeName == "disabled" or
node.nodeName == "selected"):
return True
# wxBitmap or wxIcon toplevel resources:
if node.nodeName == "object":
klass = node.getAttribute("class")
if klass == "wxBitmap" or klass == "wxIcon":
return True
return False
#-------------------------------------------------------------------
def ReplaceFilenamesInXRC(self, node, files, resourcePath):
""" Finds all files mentioned in resource file, e.g. <bitmap>filename</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 != minidom.Document.ELEMENT_NODE: return
containsFilename = self.NodeContainsFilename(node);
for n in node.childNodes:
if (containsFilename and
(n.nodeType == minidom.Document.TEXT_NODE or
n.nodeType == minidom.Document.CDATA_SECTION_NODE)):
filename = n.nodeValue
memoryFilename = self.GetMemoryFilename(filename)
n.nodeValue = memoryFilename
if filename not in files:
files.append(filename)
# Recurse into children
if n.nodeType == minidom.Document.ELEMENT_NODE:
self.ReplaceFilenamesInXRC(n, files, resourcePath);
#-------------------------------------------------------------------
def FindStringsInNode(self, parent):
def is_number(st):
try:
i = int(st)
return True
except ValueError:
return False
strings = []
if parent is None:
return strings;
for child in parent.childNodes:
if ((parent.nodeType == parent.ELEMENT_NODE) and
# parent is an element, i.e. has subnodes...
(child.nodeType == child.TEXT_NODE or
child.nodeType == child.CDATA_SECTION_NODE) and
# ...it is textnode...
(
parent.tagName == "label" or
(parent.tagName == "value" and
not is_number(child.nodeValue)) or
parent.tagName == "help" or
parent.tagName == "longhelp" or
parent.tagName == "tooltip" or
parent.tagName == "htmlcode" or
parent.tagName == "title" or
parent.tagName == "item"
)):
# ...and known to contain translatable string
if (parent.getAttribute("translate") != "0"):
strings.append(self.ConvertText(child.nodeValue))
# subnodes:
if child.nodeType == child.ELEMENT_NODE:
strings += self.FindStringsInNode(child)
return strings
#-------------------------------------------------------------------
def ConvertText(self, st):
st2 = ""
dt = list(st)
skipNext = False
for i in range(len(dt)):
if skipNext:
skipNext = False
continue
if dt[i] == '_':
if dt[i+1] == '_':
st2 += '_'
skipNext = True
else:
st2 += '&'
elif dt[i] == '\n':
st2 += '\\n'
elif dt[i] == '\t':
st2 += '\\t'
elif dt[i] == '\r':
st2 += '\\r'
elif dt[i] == '\\':
if dt[i+1] not in ['n', 't', 'r']:
st2 += '\\\\'
else:
st2 += '\\'
elif dt[i] == '"':
st2 += '\\"'
else:
st2 += dt[i]
return st2
#---------------------------------------------------------------------------
def main(args):
resourceFilename = ""
outputFilename = ""
embedResources = False
generateGetText = False
try:
opts, args = getopt.gnu_getopt(args, "hego:", "help embed gettext output=".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 args:
resourceFilename = args[0]
else:
print __doc__
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 ["-o", "--output"]:
outputFilename = val
if opt in ["-e", "--embed"]:
embedResources = True
if opt in ["-g", "--gettext"]:
generateGetText = True
if outputFilename is None or outputFilename == "":
outputFilename = os.path.splitext(resourceFilename)[0] + "_xrc.py"
comp = XmlResourceCompiler()
try:
comp.MakePythonModule(resourceFilename, outputFilename, embedResources, generateGetText)
except IOError, e:
print >>sys.stderr, "%s." % str(e)
else:
if outputFilename != "-":
print >>sys.stderr, "Resources written to %s." % outputFilename
if __name__ == "__main__":
main(sys.argv[1:])