From 73b2a9a749a960c7c5150d70e847cd740983c034 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 8 Apr 2006 06:17:11 +0000 Subject: [PATCH] First pass at integrating a code generator in XRCed. Initial patch from Eli Golovinsky, with lots of additional mods by me. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38618 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/wx/tools/XRCed/globals.py | 2 +- wxPython/wx/tools/XRCed/xrced.py | 126 +++- wxPython/wx/tools/XRCed/xrced.xrc | 100 ++++ wxPython/wx/tools/pywxrc.py | 898 +++++++++-------------------- 4 files changed, 496 insertions(+), 630 deletions(-) diff --git a/wxPython/wx/tools/XRCed/globals.py b/wxPython/wx/tools/XRCed/globals.py index e731674e41..e2a470d994 100644 --- a/wxPython/wx/tools/XRCed/globals.py +++ b/wxPython/wx/tools/XRCed/globals.py @@ -21,7 +21,7 @@ MinWxVersion = (2,6,0) if wxVERSION[:3] < MinWxVersion: print '''\ ******************************* WARNING ************************************** - This version of XRCed may not work correctly on your version of wxWindows. + This version of XRCed may not work correctly on your version of wxWidgets. Please upgrade wxWindows to %d.%d.%d or higher. ******************************************************************************''' % MinWxVersion diff --git a/wxPython/wx/tools/XRCed/xrced.py b/wxPython/wx/tools/XRCed/xrced.py index ddf9add60a..78b97e54b3 100644 --- a/wxPython/wx/tools/XRCed/xrced.py +++ b/wxPython/wx/tools/XRCed/xrced.py @@ -122,6 +122,9 @@ class Frame(wxFrame): menu.AppendSeparator() menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') + self.ID_GENERATE_PYTHON = wxNewId() + menu.Append(self.ID_GENERATE_PYTHON, '&Generate Python...', + 'Generate a Python module that uses this XRC') menu.AppendSeparator() menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') @@ -222,6 +225,7 @@ class Frame(wxFrame): EVT_MENU(self, wxID_OPEN, self.OnOpen) EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) + EVT_MENU(self, self.ID_GENERATE_PYTHON, self.OnGeneratePython) EVT_MENU(self, wxID_EXIT, self.OnExit) # Edit EVT_MENU(self, wxID_UNDO, self.OnUndo) @@ -373,6 +377,20 @@ class Frame(wxFrame): else: dlg.Destroy() return + + if conf.localconf: + # if we already have a localconf then it needs to be + # copied to a new config with the new name + lc = conf.localconf + nc = self.CreateLocalConf(path) + flag, key, idx = lc.GetFirstEntry() + while flag: + nc.Write(key, lc.Read(key)) + flag, key, idx = lc.GetNextEntry(idx) + conf.localconf = nc + else: + # otherwise create a new one + conf.localconf = self.CreateLocalConf(path) else: path = self.dataFile self.SetStatusText('Saving...') @@ -384,6 +402,11 @@ class Frame(wxFrame): self.Save(tmpName) # save temporary file first shutil.move(tmpName, path) self.dataFile = path + if conf.localconf.ReadBool("autogenerate", False): + pypath = conf.localconf.Read("filename") + embed = conf.localconf.ReadBool("embedResource", False) + self.GeneratePython(self.dataFile, pypath, embed) + self.SetStatusText('Data saved') self.SaveRecent(path) except IOError: @@ -399,6 +422,28 @@ class Frame(wxFrame): EVT_MENU(self, newid, self.OnRecentFile) conf.recentfiles[newid] = path + def GeneratePython(self, dataFile, pypath, embed): + try: + import wx.tools.pywxrc + rescomp = wx.tools.pywxrc.XmlResourceCompiler() + rescomp.MakePythonModule(dataFile, pypath, embed) + except: + inf = sys.exc_info() + wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1]) + wxLogError('Error generating python code : %s' % pypath) + raise + + + def OnGeneratePython(self, evt): + if self.modified or not conf.localconf: + wx.MessageBox("Save the XRC file first!", "Error") + return + + dlg = PythonOptions(self, conf.localconf, self.dataFile) + dlg.ShowModal() + dlg.Destroy() + + def OnExit(self, evt): self.Close() @@ -1047,8 +1092,16 @@ Homepage: http://xrced.sourceforge.net\ conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() evt.Skip() + + def CreateLocalConf(self, path): + name = os.path.splitext(path)[0] + name += '.xcfg' + return wx.FileConfig(localFilename=name) + + def Clear(self): self.dataFile = '' + conf.localconf = None undoMan.Clear() self.SetModified(False) tree.Clear() @@ -1094,6 +1147,7 @@ Homepage: http://xrced.sourceforge.net\ if dir: os.chdir(dir) tree.SetData(dom) self.SetTitle(progname + ': ' + os.path.basename(path)) + conf.localconf = self.CreateLocalConf(self.dataFile) except: # Nice exception printing inf = sys.exc_info() @@ -1146,12 +1200,13 @@ Homepage: http://xrced.sourceforge.net\ self.domCopy = None self.SetModified(False) panel.SetModified(False) + conf.localconf.Flush() except: inf = sys.exc_info() wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1]) wxLogError('Error writing file: %s' % path) raise - + def AskSave(self): if not (self.modified or panel.IsModified()): return True flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE @@ -1175,6 +1230,72 @@ Homepage: http://xrced.sourceforge.net\ ################################################################################ +class PythonOptions(wx.Dialog): + + def __init__(self, parent, cfg, dataFile): + pre = wx.PreDialog() + g.frame.res.LoadOnDialog(pre, parent, "PYTHON_OPTIONS") + self.PostCreate(pre) + + self.cfg = cfg + self.dataFile = dataFile + + self.AutoGenerateCB = XRCCTRL(self, "AutoGenerateCB") + self.EmbedCB = XRCCTRL(self, "EmbedCB") + self.GettextCB = XRCCTRL(self, "GettextCB") + self.MakeXRSFileCB = XRCCTRL(self, "MakeXRSFileCB") + self.FileNameTC = XRCCTRL(self, "FileNameTC") + self.BrowseBtn = XRCCTRL(self, "BrowseBtn") + self.GenerateBtn = XRCCTRL(self, "GenerateBtn") + self.SaveOptsBtn = XRCCTRL(self, "SaveOptsBtn") + + self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseBtn) + self.Bind(wx.EVT_BUTTON, self.OnGenerate, self.GenerateBtn) + self.Bind(wx.EVT_BUTTON, self.OnSaveOpts, self.SaveOptsBtn) + + if self.cfg.Read("filename", "") != "": + self.FileNameTC.SetValue(self.cfg.Read("filename")) + else: + name = os.path.splitext(dataFile)[0] + name += '_xrc.py' + self.FileNameTC.SetValue(name) + self.AutoGenerateCB.SetValue(self.cfg.ReadBool("autogenerate", False)) + self.EmbedCB.SetValue(self.cfg.ReadBool("embedResource", False)) + self.MakeXRSFileCB.SetValue(self.cfg.ReadBool("makeXRS", False)) + self.GettextCB.SetValue(self.cfg.ReadBool("genGettext", False)) + + + def OnBrowse(self, evt): + path = self.FileNameTC.GetValue() + dirname = os.path.abspath(os.path.dirname(path)) + name = os.path.split(path)[1] + dlg = wxFileDialog(self, 'Save As', dirname, name, '*.py', + wxSAVE | wxOVERWRITE_PROMPT) + if dlg.ShowModal() == wxID_OK: + path = dlg.GetPath() + self.FileNameTC.SetValue(path) + dlg.Destroy() + + + def OnGenerate(self, evt): + pypath = self.FileNameTC.GetValue() + embed = self.EmbedCB.GetValue() + frame.GeneratePython(self.dataFile, pypath, embed) + self.OnSaveOpts() + + + def OnSaveOpts(self, evt=None): + self.cfg.Write("filename", self.FileNameTC.GetValue()) + self.cfg.WriteBool("autogenerate", self.AutoGenerateCB.GetValue()) + self.cfg.WriteBool("embedResource", self.EmbedCB.GetValue()) + self.cfg.WriteBool("makeXRS", self.MakeXRSFileCB.GetValue()) + self.cfg.WriteBool("genGettext", self.GettextCB.GetValue()) + + self.EndModal(wx.ID_OK) + + +################################################################################ + def usage(): print >> sys.stderr, 'usage: xrced [-dhiv] [file]' @@ -1210,6 +1331,7 @@ Please upgrade wxWindows to %d.%d.%d or higher.''' % MinWxVersion) # Settings global conf conf = g.conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) + conf.localconf = None conf.autoRefresh = conf.ReadInt('autorefresh', True) pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) @@ -1246,7 +1368,7 @@ Please upgrade wxWindows to %d.%d.%d or higher.''' % MinWxVersion) def OnExit(self): # Write config global conf - wc = wxConfigBase_Get() + wc = conf wc.WriteInt('autorefresh', conf.autoRefresh) wc.WriteInt('x', conf.x) wc.WriteInt('y', conf.y) diff --git a/wxPython/wx/tools/XRCed/xrced.xrc b/wxPython/wx/tools/XRCed/xrced.xrc index 70e7772a84..e80599b59f 100644 --- a/wxPython/wx/tools/XRCed/xrced.xrc +++ b/wxPython/wx/tools/XRCed/xrced.xrc @@ -511,4 +511,104 @@ + + Python Module Config + + wxVERTICAL + + + wxVERTICAL + + + + + + + + + + + + + + 0 + + + + + + 0 + + + + 1,10 + + + + + + + + + wxHORIZONTAL + + + 500,-1 + + wxALIGN_CENTRE_VERTICAL + + + 5,0 + + + + + + + + + + wxALL + 10 + + + + wxEXPAND + + + + wxHORIZONTAL + + 1,10 + + + + + + 1 + + + + 10,1 + + + + + + + + + 10,1 + + + + + + + + wxALL|wxEXPAND|wxALIGN_CENTRE_HORIZONTAL + 10 + + + \ No newline at end of file diff --git a/wxPython/wx/tools/pywxrc.py b/wxPython/wx/tools/pywxrc.py index 3a92664d7a..dbec81f2ce 100644 --- a/wxPython/wx/tools/pywxrc.py +++ b/wxPython/wx/tools/pywxrc.py @@ -7,518 +7,223 @@ # 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 -- XML resource compiler +pywxrc -- Python XML resource compiler -Usage: wxrc [-h] [-v] [-e] [-c] [-p] [-g] [-n ] [-o ] 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] +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 +import sys, os, getopt, glob, re +import xml.dom.minidom as minidom 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 PythonTemplates: + FILE_HEADER = """\ +# This file was automatically generated by pywxrc, do not edit by hand. +import wx +import wx.xrc as xrc -#---------------------------------------------------------------------- +__res = None -class XRCWndClassData: - def __init__(self, className, parentClassName, node): - self.className = className - self.parentClassName = parentClassName - self.BrowseXmlNode(node.GetChildren()) - self.wdata = [] +def get_resources(): + \"\"\" This function provides access to the XML resources in this module.\"\"\" + global __res + if __res == None: + __init_resources() + return __res +""" - 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") + CLASS_HEADER = """\ +class %(windowName)sBase(wx.%(windowClass)s): + def PreCreate(self): + \"\"\" This function is called during the class's initialization. - 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): + 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()) +""" - #-------------------------------------------------- - def GetInternalFileName(self, name, flist): - name2 = name; - name2 = name2.replace(":", "_") - name2 = name2.replace("/", "_") - name2 = name2.replace("\\", "_") - name2 = name2.replace("*", "_") - name2 = name2.replace("?", "_") + ADD_FILE_TO_MEMFS = """\ + wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s) +""" - s = os.path.split(self.parOutput)[1] + "$" + name2 + LOAD_RES_MEMFS = """\ + global __res + __res = xrc.EmptyXmlResource() + __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s') +""" - 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 +class XmlResourceCompiler: + + templates = PythonTemplates() - doc = wx.xrc.EmptyXmlDocument() + """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()) - 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. filename - 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) + # 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') - inp = open(fullname) - out = open(os.path.join(self.parOutputPath, filename), "w") - out.write(inp.read()) + return "".join(outputList) - # subnodes: - if n.GetType() == wx.xrc.XML_ELEMENT_NODE: - self.FindFilesInXML(n, flist, inputPath); + #------------------------------------------------------------------- - n = n.GetNext() - + def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument): + outputList = [] + outputList.append(self.templates.INIT_RESOURE_HEADER) - #-------------------------------------------------- - def DeleteTempFiles(self, flist): - for f in flist: - os.unlink(os.path.join(self.parOutputPath, f)) + 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()) - #-------------------------------------------------- - def MakePackageZIP(self, flist): - files = " ".join(flist) + for f in files: + filename = self.GetMemoryFilename(f) + fileData = self.FileToString(os.path.join(resourcePath, f)) + outputList.append(self.templates.FILE_AS_STRING % locals()) - if self.flagVerbose: - print "compressing %s..." % self.parOutput + 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) + + #------------------------------------------------------------------- - cwd = os.getcwd() - os.chdir(self.parOutputPath) - cmd = "zip -9 -j " - if not self.flagVerbose: - cmd += "-q " - cmd += self.parOutput + " " + files + def GenerateInitResourcesFile(self, resourceFilename, resourceDocument): + outputList = [] + outputList.append(self.templates.INIT_RESOURE_HEADER) + outputList.append(self.templates.LOAD_RES_FILE % locals()) + return "".join(outputList) - 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 = [] + 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() - 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 :( + fileLen = len(buffer) 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 - -#ifdef __BORLANDC__ - #pragma hdrstop -#endif - -#ifndef WX_PRECOMP - #include -#endif - -#include -#include -#include -#include - -""") - - 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): + for i in xrange(fileLen): s = buffer[i] c = ord(s) if s == '\n': @@ -533,182 +238,121 @@ class XmlResApp: if linelng > 70: linelng = 0 - output.append("\\\n") - - output.append(tmp) + outputList.append("\\\n") + + outputList.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) + return "".join(outputList) + #------------------------------------------------------------------- - - #-------------------------------------------------- - def FindStrings(self): - strings = [] - for pf in self.parFiles: - if self.flagVerbose: - print "processing %s..." % pf + def NodeContainsFilename(self, node): + """ Does 'node' contain filename information at all? """ - doc = wx.xrc.EmptyXmlDocument() - if not doc.Load(pf): - print "Error parsing file", pf - self.retCode = 1 - continue + # Any bitmaps: + if node.nodeName == "bitmap": + return True - strings += self.FindStringsInNode(doc.GetRoot()) + if node.nodeName == "icon": + return True - return strings - - - #-------------------------------------------------- - def ConvertText(self, st): - st2 = "" - dt = list(st) + # URLs in wxHtmlWindow: + if node.nodeName == "url": + return True - 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] + # 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 - return st2 - - - - #-------------------------------------------------- - def FindStringsInNode(self, parent): - def is_number(st): - try: - i = int(st) + # wxBitmap or wxIcon toplevel resources: + if node.nodeName == "object": + klass = node.getAttribute("class") + if klass == "wxBitmap" or klass == "wxIcon": 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"): + return False - strings.append(self.ConvertText(child.GetContent())) + #------------------------------------------------------------------- - # subnodes: - if child.GetType() == wx.xrc.XML_ELEMENT_NODE: - strings += self.FindStringsInNode(child) + 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 - child = child.GetNext() + containsFilename = self.NodeContainsFilename(node); - return strings + 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(): - XmlResApp().main(sys.argv[1:]) +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() + main(sys.argv[1:])