#---------------------------------------------------------------------- # 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 Usage: python pywxrc.py -h python pywxrc.py [-e] [-o filename] -h, --help show help message -e, --embed embed resources in 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 %(windowName)sBase(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') """ #---------------------------------------------------------------------- class XmlResourceCompiler: templates = PythonTemplates() """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) 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) #------------------------------------------------------------------- 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 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): 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. filename 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 main(args): resourceFilename = "" outputFilename = "" embedResources = False try: opts, args = getopt.gnu_getopt(args, "heo:", "help embed output=".split()) except getopt.GetoptError: 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 outputFilename is None or outputFilename == "": outputFilename = os.path.splitext(resourceFilename)[0] + "_xrc.py" comp = XmlResourceCompiler() try: comp.MakePythonModule(resourceFilename, outputFilename, embedResources) 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:])