]> git.saurik.com Git - wxWidgets.git/commitdiff
A Python version of wxrc
authorRobin Dunn <robin@alldunn.com>
Wed, 6 Oct 2004 18:32:25 +0000 (18:32 +0000)
committerRobin Dunn <robin@alldunn.com>
Wed, 6 Oct 2004 18:32:25 +0000 (18:32 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@29677 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

wxPython/scripts/CreateBatchFiles.py
wxPython/scripts/pywxrc [new file with mode: 0755]
wxPython/wx/tools/pywxrc.py [new file with mode: 0644]

index 5496a7b2900336d43c20110121118869fd87d2a4..6fb1d8780b6dacf76d4c19d2ba5d841134a30cec 100644 (file)
@@ -27,6 +27,7 @@ scripts = [ ("img2png",    0),
             ("pyalamode",  1),
             ("pyalacarte", 1),
             ("helpviewer", 1),
+            ("pywxrc",     0),
             ]
 
 template = """\
diff --git a/wxPython/scripts/pywxrc b/wxPython/scripts/pywxrc
new file mode 100755 (executable)
index 0000000..066caa8
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+from wx.tools.pywxrc import main
+main()
+
diff --git a/wxPython/wx/tools/pywxrc.py b/wxPython/wx/tools/pywxrc.py
new file mode 100644 (file)
index 0000000..178d45a
--- /dev/null
@@ -0,0 +1,711 @@
+#----------------------------------------------------------------------
+# 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
+
+        # 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()
+