X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/73b2a9a749a960c7c5150d70e847cd740983c034..30e24d9dd5a5abf1c9a375ea259eb239b122b7f7:/wxPython/wx/tools/pywxrc.py diff --git a/wxPython/wx/tools/pywxrc.py b/wxPython/wx/tools/pywxrc.py index dbec81f2ce..9093b2e125 100644 --- a/wxPython/wx/tools/pywxrc.py +++ b/wxPython/wx/tools/pywxrc.py @@ -16,13 +16,16 @@ """ pywxrc -- Python XML resource compiler + (see http://wiki.wxpython.org/index.cgi/pywxrc for more info) Usage: python pywxrc.py -h - python pywxrc.py [-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,8 +55,8 @@ def get_resources(): """ CLASS_HEADER = """\ -class %(windowName)sBase(wx.%(windowClass)s): - def PreCreate(self): +class xrc%(windowName)s(wx.%(windowClass)s): + 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 @@ -62,8 +66,8 @@ class %(windowName)sBase(wx.%(windowClass)s): def __init__(self, parent): # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.Pre%(windowClass)s() + self.PreCreate(pre) get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s") - self.PreCreate() self.PostCreate(pre) # Define variables for the controls @@ -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 in ["Menu", "MenuItem"]: + 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: