]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/tools/pywxrc.py
little tweaks
[wxWidgets.git] / wxPython / wx / tools / pywxrc.py
index dbec81f2cefce48a9fab5631dd8b536c2c7a87c1..f26fb00f4f20924be58ea8e3dc5d1235abac5386 100644 (file)
 
 """
 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] [-o filename]
+       python pywxrc.py [-p] [-g] [-e] [-o filename] xrc input files... 
        
-  -h, --help    show help message
-  -e, --embed   embed resources in output file
-  -o, --output  output filename, or - for stdout
+  -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
@@ -35,6 +38,7 @@ import wx.xrc
 class PythonTemplates:
     FILE_HEADER = """\
 # This file was automatically generated by pywxrc, do not edit by hand.
+# -*- coding: UTF-8 -*-
 
 import wx
 import wx.xrc as xrc
@@ -51,7 +55,7 @@ def get_resources():
 """
 
     CLASS_HEADER = """\
-class %(windowName)sBase(wx.%(windowClass)s):
+class xrc%(windowName)s(wx.%(windowClass)s):
     def PreCreate(self):
         \"\"\" This function is called during the class's initialization.
         
@@ -73,29 +77,46 @@ class %(windowName)sBase(wx.%(windowClass)s):
         self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
 """
 
+    MENU_CLASS_HEADER = """\
+class xrc%(windowName)s(wx.%(windowClass)s):
+    def __init__(self):
+        pre = get_resources().LoadMenu("%(windowName)s")
+
+        # This is a copy of Robin's PostCreate voodoo magic in wx.Window that
+        # relinks the self object with the menu object.
+        self.this = pre.this
+        self.thisown = pre.thisown
+        pre.thisown = 0
+        if hasattr(self, '_setOORInfo'):
+            self._setOORInfo(self)
+        if hasattr(self, '_setCallbackInfo'):
+            self._setCallbackInfo(self, self.__class__)
+
+        # Define variables for the menu items
+"""
+
+    CREATE_MENUITEM_VAR = """\
+        self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
+"""
+
     INIT_RESOURE_HEADER = """\
-# -------------------------------------------------------------
 # ------------------------ Resource data ----------------------
-# -------------------------------------------------------------
 
 def __init_resources():
+    global __res
+    __res = xrc.EmptyXmlResource()
 """
 
     LOAD_RES_FILE = """\
-    global __res
-    __res = xrc.XmlResource('%(resourceFilename)s')
-"""
+    __res.Load('%(resourceFilename)s')"""
 
     FILE_AS_STRING = """\
     %(filename)s = '''\\
 %(fileData)s'''
 
-
 """
 
     PREPARE_MEMFS = """\
-    # Load all the strings as memory files
-    
     wx.FileSystem.AddHandler(wx.MemoryFSHandler())
 """
 
@@ -104,11 +125,24 @@ def __init_resources():
 """
 
     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
+    
+%s
+"""
+
 #----------------------------------------------------------------------
 
 class XmlResourceCompiler:
@@ -117,23 +151,66 @@ class XmlResourceCompiler:
 
     """This class generates Python code from XML resource files (XRC)."""
 
-    def MakePythonModule(self, resourceFilename, outputFilename, embedResources=False):
-        if outputFilename == "-":
-            outputFile = sys.stdout
-        else:
-            try:
-                outputFile = open(outputFilename, "wt")
-            except IOError:
-                raise IOError("Can't write output to '%s'" % outputFilename)
+    def MakePythonModule(self, inputFiles, outputFilename,
+                         embedResources=False, generateGetText=False):
+
+        outputFile = self._OpenOutputFile(outputFilename)
+
+        classes = []
+        resources = []
+        gettextStrings = []
 
-        resourceDocument = minidom.parse(resourceFilename)
+        # process all the inputFiles, collecting the output data
+        for inFile in inputFiles:
+            resourceDocument = minidom.parse(inFile)
+            classes.append(self.GenerateClasses(resourceDocument))
+
+            if embedResources:
+                res = self.GenerateInitResourcesEmbedded(inFile, resourceDocument)
+            else:
+                res = self.GenerateInitResourcesFile(inFile, resourceDocument)
+            resources.append(res)
+
+            if generateGetText:
+                gettextStrings += self.FindStringsInNode(resourceDocument.firstChild)
+                
+        # now write it all out
         print >>outputFile, self.templates.FILE_HEADER
-        print >>outputFile, self.GenerateClasses(resourceDocument)
-        
+
+        # 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.
+        classes = u"\n".join(classes)
+        print >>outputFile, classes.encode("UTF-8")
+
+        print >>outputFile, self.templates.INIT_RESOURE_HEADER
         if embedResources:
-            print >>outputFile, self.GenerateInitResourcesEmbedded(resourceFilename, resourceDocument)
-        else:
-            print >>outputFile, self.GenerateInitResourcesFile(resourceFilename, resourceDocument)
+            print >>outputFile, self.templates.PREPARE_MEMFS
+        resources = u"\n".join(resources)
+        print >>outputFile, resources.encode("UTF-8")
+
+        if generateGetText:
+            # These have already been converted to utf-8...
+            gettextStrings = ['    _("%s")' % s for s in gettextStrings]
+            gettextStrings = "\n".join(gettextStrings)
+            print >>outputFile, self.templates.GETTEXT_DUMMY_FUNC % gettextStrings
+
+    #-------------------------------------------------------------------
+
+    def MakeGetTextOutput(self, inputFiles, outputFilename):
+        """
+        Just output the gettext strings by themselves, with no other
+        code generation.
+        """
+        outputFile = self._OpenOutputFile(outputFilename)
+        for inFile in inputFiles:
+            resourceDocument = minidom.parse(inFile)
+            resource = resourceDocument.firstChild
+            strings = self.FindStringsInNode(resource)
+            strings = ['_("%s");' % s for s in strings]
+            print >>outputFile, "\n".join(strings)
 
     #-------------------------------------------------------------------
 
@@ -149,7 +226,11 @@ class XmlResourceCompiler:
             windowClass = topWindow.getAttribute("class")
             windowClass = re.sub("^wx", "", windowClass)
             windowName = topWindow.getAttribute("name")
-            outputList.append(self.templates.CLASS_HEADER % locals())
+            
+            if windowClass == "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 handlers
             # for standard controls.
@@ -157,11 +238,12 @@ class XmlResourceCompiler:
                 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())
+                if (widgetName != "" and widgetClass != ""):
+                    if widgetClass == "MenuItem":
+                        outputList.append(self.templates.CREATE_MENUITEM_VAR % locals())
+                    elif widgetClass not in \
+                        ['tool', 'unknown', 'notebookpage', 'separator', 'sizeritem']:
+                        outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
             outputList.append('\n\n')
                     
         return "".join(outputList)
@@ -170,9 +252,6 @@ class XmlResourceCompiler:
 
     def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
         outputList = []
-
-        outputList.append(self.templates.INIT_RESOURE_HEADER)
-
         files = []
 
         resourcePath = os.path.split(resourceFilename)[0]
@@ -182,7 +261,7 @@ class XmlResourceCompiler:
         self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
         
         filename = resourceFilename
-        fileData = resourceDocument.toxml()
+        fileData = resourceDocument.toxml() # what about this? encoding=resourceDocument.encoding)
         outputList.append(self.templates.FILE_AS_STRING % locals())
 
         for f in files:
@@ -190,8 +269,6 @@ class XmlResourceCompiler:
             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())
@@ -203,8 +280,9 @@ class XmlResourceCompiler:
     #-------------------------------------------------------------------
 
     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)
 
@@ -308,24 +386,122 @@ class XmlResourceCompiler:
             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.encode("UTF-8")                
+
+
+    #-------------------------------------------------------------------
+
+    def _OpenOutputFile(self, outputFilename):
+        if outputFilename == "-":
+            outputFile = sys.stdout
+        else:
+            try:
+                outputFile = open(outputFilename, "wt")
+            except IOError:
+                raise IOError("Can't write output to '%s'" % outputFilename)
+        return outputFile
+
+    
+
+
+
 #---------------------------------------------------------------------------
 
 def main(args):
     resourceFilename = ""
-    outputFilename = ""
+    outputFilename = None
     embedResources = False
+    generateGetText = False
+    generatePython = False
 
     try:
-        opts, args = getopt.gnu_getopt(args, "heo:", "help embed output=".split())
-    except getopt.GetoptError:
+        opts, args = getopt.gnu_getopt(args,
+                                       "hpgeo:",
+                                       "help python gettext embed 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:
+    if not args:
         print __doc__
+        print "No xrc input file was specified."
         sys.exit(1)
 
     # Parse options and arguments
@@ -334,19 +510,45 @@ def main(args):
             print __doc__
             sys.exit(1)
 
+        if opt in ["-p", "--python"]:
+            generatePython = True
+
         if opt in ["-o", "--output"]:
             outputFilename = val
             
         if opt in ["-e", "--embed"]:
             embedResources = True
 
-    if outputFilename is None or outputFilename == "":
-        outputFilename = os.path.splitext(resourceFilename)[0] + "_xrc.py"
+        if opt in ["-g", "--gettext"]:
+            generateGetText = True
+
+
+    # check for and expand any wildcards in the list of input files
+    inputFiles = []
+    for arg in args:
+        inputFiles += glob.glob(arg)
+
 
     comp = XmlResourceCompiler()
     
     try:
-        comp.MakePythonModule(resourceFilename, outputFilename, embedResources)
+        if generatePython:
+            if not outputFilename:
+                outputFilename = os.path.splitext(args[0])[0] + "_xrc.py"
+            comp.MakePythonModule(inputFiles, outputFilename,
+                                  embedResources, generateGetText)
+
+        elif generateGetText:
+            if not outputFilename:
+                outputFilename = '-'
+            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: