7aada1e05a
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@30439 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
715 lines
21 KiB
Python
715 lines
21 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
|
|
#
|
|
# RCS-ID: $Id$
|
|
# Copyright: (c) 2004 by Total Control Software, 2000 Vaclav Slavik
|
|
# Licence: wxWindows license
|
|
#----------------------------------------------------------------------
|
|
|
|
"""
|
|
pywxrc -- XML resource compiler
|
|
|
|
Usage: wxrc [-h] [-v] [-e] [-c] [-p] [-g] [-n <str>] [-o <str>] input file(s)...
|
|
-h, --help show help message
|
|
-v, --verbose be verbose
|
|
-e, --extra-cpp-code output C++ header file with XRC derived classes
|
|
-c, --cpp-code output C++ source rather than .xrs file
|
|
-p, --python-code output wxPython source rather than .rsc file
|
|
-g, --gettext output list of translatable strings (to stdout or file if -o used)
|
|
-n, --function str C++/Python function name (with -c or -p) [InitXmlResource]
|
|
-o, --output str output file [resource.xrs/cpp/py]
|
|
"""
|
|
|
|
import sys, os, getopt, glob
|
|
import wx
|
|
import wx.xrc
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
class XRCWidgetData:
|
|
def __init__(self, vname, vclass):
|
|
self.name = vname
|
|
self.klass = vclass
|
|
def GetName(self):
|
|
return self.name
|
|
def GetClass(self):
|
|
return self.klass
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
class XRCWndClassData:
|
|
def __init__(self, className, parentClassName, node):
|
|
self.className = className
|
|
self.parentClassName = parentClassName
|
|
self.BrowseXmlNode(node.GetChildren())
|
|
self.wdata = []
|
|
|
|
|
|
def BrowseXmlNode(self, node):
|
|
while node:
|
|
if node.GetName() == "object" and node.HasProp("class") and node.HasProp("name"):
|
|
classVal = node.GetPropVal("class", "")
|
|
nameVal = node.GetPropVal("name", "")
|
|
self.wdata.append(XRCWidgetData(nameVal, classVal))
|
|
children = node.GetChildren()
|
|
if children:
|
|
self.BrowseXmlNode(children)
|
|
node = node.GetNext()
|
|
|
|
|
|
def GetWidgetData(self):
|
|
return self.wdata
|
|
|
|
|
|
def IsRealClass(self, name):
|
|
if name in ['tool', 'unknown', 'notebookpage', 'separator',
|
|
'sizeritem', 'wxMenuItem']:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
def GenerateHeaderCode(self, file):
|
|
file.write("class %s : public %s {\nprotected:\n" % (self.className, self.parentClassName))
|
|
|
|
for w in self.wdata:
|
|
if not self.IsRealClass(w.GetClass()):
|
|
continue
|
|
if not w.GetName():
|
|
continue
|
|
file.write(" " + w.GetClass() + "* " + w.GetName() + ";\n")
|
|
|
|
file.write("\nprivate:\n void InitWidgetsFromXRC(){\n",
|
|
+ " wxXmlResource::Get()->LoadObject(this,NULL,\""
|
|
+ self.className
|
|
+ "\",\""
|
|
+ self.parentClassName
|
|
+ "\");\n");
|
|
|
|
for w in self.wdata:
|
|
if not self.IsRealClass(w.GetClass()):
|
|
continue
|
|
if not w.GetName():
|
|
continue
|
|
file.write( " "
|
|
+ w.GetName()
|
|
+ " = XRCCTRL(*this,\""
|
|
+ w.GetName()
|
|
+ "\","
|
|
+ w.GetClass()
|
|
+ ");\n")
|
|
|
|
file.write(" }\n")
|
|
file.write("public:\n"
|
|
+ self.className
|
|
+ "::"
|
|
+ self.className
|
|
+ "(){\n"
|
|
+ " InitWidgetsFromXRC();\n"
|
|
+ " }\n"
|
|
+ "};\n")
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
class XmlResApp:
|
|
def __init__(self):
|
|
self.flagVerbose = False
|
|
self.flagCPP = False
|
|
self.flagH = False
|
|
self.flagPython = False
|
|
self.flagGettext = False
|
|
self.parOutput = ""
|
|
self.parFuncname = "InitXmlResource"
|
|
self.parFiles = []
|
|
self.aXRCWndClassData = []
|
|
|
|
|
|
#--------------------------------------------------
|
|
def main(self, args):
|
|
try:
|
|
opts, args = getopt.getopt(args, "hvecpgn:o:",
|
|
"help verbose extra-cpp-code cpp-code python-code gettext function= output=".split())
|
|
except getopt.GetoptError:
|
|
print __doc__
|
|
sys.exit(1)
|
|
|
|
for opt, val in opts:
|
|
if opt in ["-h", "--help"]:
|
|
print __doc__
|
|
sys.exit(1)
|
|
|
|
if opt in ["-v", "--verbose"]:
|
|
self.flagVerbose = True
|
|
|
|
if opt in ["-e", "--extra-cpp-code"]:
|
|
self.flagH = True
|
|
|
|
if opt in ["-c", "--cpp-code"]:
|
|
self.flagCPP = True
|
|
|
|
if opt in ["-p", "--python-code"]:
|
|
self.flagPython = True
|
|
|
|
if opt in ["-g", "--gettext"]:
|
|
self.flagGettext = True
|
|
|
|
if opt in ["-n", "--function"]:
|
|
self.parFuncname = val
|
|
|
|
if opt in ["-o", "--output"]:
|
|
self.parOutput = val
|
|
|
|
if self.flagCPP + self.flagPython + self.flagGettext == 0:
|
|
print __doc__
|
|
print "\nYou must specify one of -c, -p or -g!\n"
|
|
sys.exit(1)
|
|
|
|
if self.flagCPP + self.flagPython + self.flagGettext > 1:
|
|
print __doc__
|
|
print "\n-c, -p and -g are mutually exclusive, specify only 1!\n"
|
|
sys.exit(1)
|
|
|
|
|
|
if self.parOutput:
|
|
self.parOutput = os.path.normpath(self.parOutput)
|
|
self.parOutputPath = os.path.split(self.parOutput)[0]
|
|
else:
|
|
self.parOutputPath = "."
|
|
if self.flagCPP:
|
|
self.parOutput = "resource.cpp"
|
|
elif self.flagPython:
|
|
self.parOutput = "resource.py"
|
|
elif self.flagGettext:
|
|
self.parOutput = ""
|
|
else:
|
|
self.parOutput = "resource.xrs"
|
|
|
|
if not args:
|
|
print __doc__
|
|
sys.exit(1)
|
|
for arg in args:
|
|
self.parFiles += glob.glob(arg)
|
|
|
|
self.retCode = 0
|
|
if self.flagGettext:
|
|
self.OutputGettext()
|
|
else:
|
|
self.CompileRes()
|
|
|
|
|
|
|
|
#--------------------------------------------------
|
|
def CompileRes(self):
|
|
files = self.PrepareTempFiles()
|
|
try:
|
|
os.unlink(self.parOutput)
|
|
except OSError:
|
|
pass
|
|
|
|
if not self.retCode:
|
|
if self.flagCPP:
|
|
self.MakePackageCPP(files)
|
|
if self.flagH:
|
|
self.GenCPPHeader()
|
|
|
|
elif self.flagPython:
|
|
self.MakePackagePython(files)
|
|
|
|
else:
|
|
self.MakePackageZIP(files)
|
|
|
|
self.DeleteTempFiles(files)
|
|
|
|
|
|
#--------------------------------------------------
|
|
def OutputGettext(self):
|
|
pass
|
|
|
|
|
|
#--------------------------------------------------
|
|
def GetInternalFileName(self, name, flist):
|
|
name2 = name;
|
|
name2 = name2.replace(":", "_")
|
|
name2 = name2.replace("/", "_")
|
|
name2 = name2.replace("\\", "_")
|
|
name2 = name2.replace("*", "_")
|
|
name2 = name2.replace("?", "_")
|
|
|
|
s = os.path.split(self.parOutput)[1] + "$" + name2
|
|
|
|
if os.path.exists(s) and s not in flist:
|
|
i = 0
|
|
while True:
|
|
s = os.path.split(self.parOutput)[1] + ("$%s%03d" % (name2, i))
|
|
if not os.path.exists(s) or s in flist:
|
|
break
|
|
return s;
|
|
|
|
|
|
#--------------------------------------------------
|
|
def PrepareTempFiles(self):
|
|
flist = []
|
|
for f in self.parFiles:
|
|
if self.flagVerbose:
|
|
print "processing %s..." % f
|
|
|
|
doc = wx.xrc.EmptyXmlDocument()
|
|
|
|
if not doc.Load(f):
|
|
print "Error parsing file", f
|
|
self.retCode = 1
|
|
continue
|
|
|
|
path, name = os.path.split(f)
|
|
name, ext = os.path.splitext(name)
|
|
|
|
self.FindFilesInXML(doc.GetRoot(), flist, path)
|
|
if self.flagH:
|
|
node = doc.GetRoot().GetChildren()
|
|
while node:
|
|
if node.GetName() == "object" and node.HasProp("class") and node.HasProp("name"):
|
|
classVal = node.GetPropVal("class", "")
|
|
nameVal = node.GetPropVal("name", "")
|
|
self.aXRCWndClassData.append(XRCWidgetData(nameVal, classVal))
|
|
node = node.GetNext()
|
|
internalName = self.GetInternalFileName(f, flist)
|
|
|
|
doc.Save(os.path.join(self.parOutputPath, internalName))
|
|
flist.append(internalName)
|
|
|
|
return flist
|
|
|
|
|
|
#--------------------------------------------------
|
|
# Does 'node' contain filename information at all?
|
|
def NodeContainsFilename(self, node):
|
|
# Any bitmaps:
|
|
if node.GetName() == "bitmap":
|
|
return True
|
|
|
|
if node.GetName() == "icon":
|
|
return True
|
|
|
|
# URLs in wxHtmlWindow:
|
|
if node.GetName() == "url":
|
|
return True
|
|
|
|
# wxBitmapButton:
|
|
parent = node.GetParent()
|
|
if parent != None and \
|
|
parent.GetPropVal("class", "") == "wxBitmapButton" and \
|
|
(node.GetName() == "focus" or node.etName() == "disabled" or
|
|
node.GetName() == "selected"):
|
|
return True
|
|
|
|
# wxBitmap or wxIcon toplevel resources:
|
|
if node.GetName() == "object":
|
|
klass = node.GetPropVal("class", "")
|
|
if klass == "wxBitmap" or klass == "wxIcon":
|
|
return True
|
|
|
|
return False
|
|
|
|
#--------------------------------------------------
|
|
# find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
|
|
def FindFilesInXML(self, node, flist, inputPath):
|
|
# Is 'node' XML node element?
|
|
if node is None: return
|
|
if node.GetType() != wx.xrc.XML_ELEMENT_NODE: return
|
|
|
|
containsFilename = self.NodeContainsFilename(node);
|
|
|
|
n = node.GetChildren()
|
|
while n:
|
|
if (containsFilename and
|
|
(n.GetType() == wx.xrc.XML_TEXT_NODE or
|
|
n.GetType() == wx.xrc.XML_CDATA_SECTION_NODE)):
|
|
|
|
if os.path.isabs(n.GetContent()) or inputPath == "":
|
|
fullname = n.GetContent()
|
|
else:
|
|
fullname = os.path.join(inputPath, n.GetContent())
|
|
|
|
if self.flagVerbose:
|
|
print "adding %s..." % fullname
|
|
|
|
filename = self.GetInternalFileName(n.GetContent(), flist)
|
|
n.SetContent(filename)
|
|
|
|
if filename not in flist:
|
|
flist.append(filename)
|
|
|
|
inp = open(fullname)
|
|
out = open(os.path.join(self.parOutputPath, filename), "w")
|
|
out.write(inp.read())
|
|
|
|
# subnodes:
|
|
if n.GetType() == wx.xrc.XML_ELEMENT_NODE:
|
|
self.FindFilesInXML(n, flist, inputPath);
|
|
|
|
n = n.GetNext()
|
|
|
|
|
|
|
|
#--------------------------------------------------
|
|
def DeleteTempFiles(self, flist):
|
|
for f in flist:
|
|
os.unlink(os.path.join(self.parOutputPath, f))
|
|
|
|
|
|
#--------------------------------------------------
|
|
def MakePackageZIP(self, flist):
|
|
files = " ".join(flist)
|
|
|
|
if self.flagVerbose:
|
|
print "compressing %s..." % self.parOutput
|
|
|
|
cwd = os.getcwd()
|
|
os.chdir(self.parOutputPath)
|
|
cmd = "zip -9 -j "
|
|
if not self.flagVerbose:
|
|
cmd += "-q "
|
|
cmd += self.parOutput + " " + files
|
|
|
|
from distutils.spawn import spawn
|
|
try:
|
|
spawn(cmd.split())
|
|
success = True
|
|
except:
|
|
success = False
|
|
|
|
os.chdir(cwd)
|
|
|
|
if not success:
|
|
print "Unable to execute zip program. Make sure it is in the path."
|
|
print "You can download it at http://www.cdrom.com/pub/infozip/"
|
|
self.retCode = 1
|
|
|
|
|
|
#--------------------------------------------------
|
|
def FileToCppArray(self, filename, num):
|
|
output = []
|
|
buffer = open(filename, "rb").read()
|
|
lng = len(buffer)
|
|
|
|
output.append("static size_t xml_res_size_%d = %d;\n" % (num, lng))
|
|
output.append("static unsigned char xml_res_file_%d[] = {\n" % num)
|
|
# we cannot use string literals because MSVC is dumb wannabe compiler
|
|
# with arbitrary limitation to 2048 strings :(
|
|
|
|
linelng = 0
|
|
for i in xrange(lng):
|
|
tmp = "%i" % ord(buffer[i])
|
|
if i != 0: output.append(',')
|
|
if linelng > 70:
|
|
linelng = 0
|
|
output.append("\n")
|
|
|
|
output.append(tmp)
|
|
linelng += len(tmp)+1
|
|
|
|
output.append("};\n\n")
|
|
|
|
return "".join(output)
|
|
|
|
|
|
|
|
#--------------------------------------------------
|
|
def MakePackageCPP(self, flist):
|
|
file = open(self.parOutput, "wt")
|
|
|
|
if self.flagVerbose:
|
|
print "creating C++ source file %s..." % self.parOutput
|
|
|
|
file.write("""\
|
|
//
|
|
// This file was automatically generated by wxrc, do not edit by hand.
|
|
//
|
|
|
|
#include <wx/wxprec.h>
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/wx.h>
|
|
#endif
|
|
|
|
#include <wx/filesys.h>
|
|
#include <wx/fs_mem.h>
|
|
#include <wx/xrc/xmlres.h>
|
|
#include <wx/xrc/xh_all.h>
|
|
|
|
""")
|
|
|
|
num = 0
|
|
for f in flist:
|
|
file.write(self.FileToCppArray(os.path.join(self.parOutputPath, f), num))
|
|
num += 1
|
|
|
|
|
|
file.write("void " + self.parFuncname + "()\n")
|
|
file.write("""\
|
|
{
|
|
|
|
// Check for memory FS. If not present, load the handler:
|
|
{
|
|
wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/dummy_file\"), wxT(\"dummy one\"));
|
|
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);
|
|
}
|
|
""");
|
|
|
|
for i in range(len(flist)):
|
|
file.write(" wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/" + flist[i])
|
|
file.write("\"), xml_res_file_%i, xml_res_size_%i);\n" %(i, i))
|
|
|
|
|
|
for i in range(len(self.parFiles)):
|
|
file.write(" wxXmlResource::Get()->Load(wxT(\"memory:XRC_resource/" +
|
|
self.GetInternalFileName(self.parFiles[i], flist) +
|
|
"\"));\n")
|
|
|
|
file.write("}\n")
|
|
|
|
|
|
#--------------------------------------------------
|
|
def GenCPPHeader(self):
|
|
path, name = os.path.split(self.parOutput)
|
|
name, ext = os.path.splitext(name)
|
|
heaFileName = name+'.h'
|
|
|
|
file = open(heaFileName, "wt")
|
|
file.write("""\
|
|
//
|
|
// This file was automatically generated by wxrc, do not edit by hand.
|
|
//
|
|
""");
|
|
file.write("#ifndef __" + name + "_h__\n")
|
|
file.write("#define __" + name + "_h__\n")
|
|
|
|
for data in self.aXRCWndClassData:
|
|
data.GenerateHeaderCode(file)
|
|
|
|
file.write("\nvoid \n" + self.parFuncname + "();\n#endif\n")
|
|
|
|
|
|
#--------------------------------------------------
|
|
def FileToPythonArray(self, filename, num):
|
|
output = []
|
|
buffer = open(filename, "rb").read()
|
|
lng = len(buffer)
|
|
|
|
output.append(" xml_res_file_%d = '''\\\n" % num)
|
|
|
|
linelng = 0
|
|
for i in xrange(lng):
|
|
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
|
|
output.append("\\\n")
|
|
|
|
output.append(tmp)
|
|
linelng += len(tmp)
|
|
|
|
output.append("'''\n\n")
|
|
|
|
return "".join(output)
|
|
|
|
#--------------------------------------------------
|
|
def MakePackagePython(self, flist):
|
|
file = open(self.parOutput, "wt")
|
|
|
|
if self.flagVerbose:
|
|
print "creating Python source file %s..." % self.parOutput
|
|
|
|
file.write("""\
|
|
#
|
|
# This file was automatically generated by wxrc, do not edit by hand.
|
|
#
|
|
|
|
import wx
|
|
import wx.xrc
|
|
|
|
""")
|
|
file.write("def " + self.parFuncname + "():\n")
|
|
|
|
num = 0
|
|
for f in flist:
|
|
file.write(self.FileToPythonArray(os.path.join(self.parOutputPath, f), num))
|
|
num += 1
|
|
|
|
file.write("""
|
|
|
|
# check if the memory filesystem handler has been loaded yet, and load it if not
|
|
wx.MemoryFSHandler.AddFile('XRC_resource/dummy_file', 'dummy value')
|
|
fsys = wx.FileSystem()
|
|
f = fsys.OpenFile('memory:XRC_resource/dummy_file')
|
|
wx.MemoryFSHandler.RemoveFile('XRC_resource/dummy_file')
|
|
if f is not None:
|
|
f.Destroy()
|
|
else:
|
|
wx.FileSystem.AddHandler(wx.MemoryFSHandler())
|
|
|
|
# load all the strings as memory files and load into XmlRes
|
|
""")
|
|
|
|
for i in range(len(flist)):
|
|
file.write(" wx.MemoryFSHandler.AddFile('XRC_resource/" + flist[i] +
|
|
"', xml_res_file_%i)\n" % i)
|
|
|
|
for pf in self.parFiles:
|
|
file.write(" wx.xrc.XmlResource.Get().Load('memory:XRC_resource/" +
|
|
self.GetInternalFileName(pf, flist) + "')\n")
|
|
|
|
|
|
#--------------------------------------------------
|
|
def OutputGettext(self):
|
|
strings = self.FindStrings()
|
|
|
|
if not self.parOutput:
|
|
out = sys.stdout
|
|
else:
|
|
out = open(self.parOutput, "wt")
|
|
|
|
for st in strings:
|
|
out.write("_(\"%s\")\n" % st)
|
|
|
|
|
|
|
|
#--------------------------------------------------
|
|
def FindStrings(self):
|
|
strings = []
|
|
for pf in self.parFiles:
|
|
if self.flagVerbose:
|
|
print "processing %s..." % pf
|
|
|
|
doc = wx.xrc.EmptyXmlDocument()
|
|
if not doc.Load(pf):
|
|
print "Error parsing file", pf
|
|
self.retCode = 1
|
|
continue
|
|
|
|
strings += self.FindStringsInNode(doc.GetRoot())
|
|
|
|
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 FindStringsInNode(self, parent):
|
|
def is_number(st):
|
|
try:
|
|
i = int(st)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
strings = []
|
|
if parent is None:
|
|
return strings;
|
|
child = parent.GetChildren()
|
|
|
|
while child:
|
|
if ((parent.GetType() == wx.xrc.XML_ELEMENT_NODE) and
|
|
# parent is an element, i.e. has subnodes...
|
|
(child.GetType() == wx.xrc.XML_TEXT_NODE or
|
|
child.GetType() == wx.xrc.XML_CDATA_SECTION_NODE) and
|
|
# ...it is textnode...
|
|
(
|
|
parent.GetName() == "label" or
|
|
(parent.GetName() == "value" and
|
|
not is_number(child.GetContent())) or
|
|
parent.GetName() == "help" or
|
|
parent.GetName() == "longhelp" or
|
|
parent.GetName() == "tooltip" or
|
|
parent.GetName() == "htmlcode" or
|
|
parent.GetName() == "title" or
|
|
parent.GetName() == "item"
|
|
)):
|
|
# ...and known to contain translatable string
|
|
if (not self.flagGettext or
|
|
parent.GetPropVal("translate", "1") != "0"):
|
|
|
|
strings.append(self.ConvertText(child.GetContent()))
|
|
|
|
# subnodes:
|
|
if child.GetType() == wx.xrc.XML_ELEMENT_NODE:
|
|
strings += self.FindStringsInNode(child)
|
|
|
|
child = child.GetNext()
|
|
|
|
return strings
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
def main():
|
|
XmlResApp().main(sys.argv[1:])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|