]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/tools/pywxrc.py
clarified the rules for determining whether the path is absolute or relative (patch...
[wxWidgets.git] / wxPython / wx / tools / pywxrc.py
CommitLineData
2e0ae50e
RD
1#----------------------------------------------------------------------
2# Name: wx.tools.pywxrc
3# Purpose: XML resource compiler
4#
5# Author: Robin Dunn
6# Based on wxrc.cpp by Vaclav Slavik, Eduardo Marques
7# Ported to Python in order to not require yet another
8# binary in wxPython distributions
9#
73b2a9a7
RD
10# Massive rework by Eli Golovinsky
11#
2e0ae50e
RD
12# RCS-ID: $Id$
13# Copyright: (c) 2004 by Total Control Software, 2000 Vaclav Slavik
14# Licence: wxWindows license
15#----------------------------------------------------------------------
16
17"""
73b2a9a7
RD
18pywxrc -- Python XML resource compiler
19
20Usage: python pywxrc.py -h
21 python pywxrc.py <resource.xrc> [-e] [-o filename]
22
23 -h, --help show help message
24 -e, --embed embed resources in output file
25 -o, --output output filename, or - for stdout
2e0ae50e
RD
26"""
27
73b2a9a7
RD
28import sys, os, getopt, glob, re
29import xml.dom.minidom as minidom
2e0ae50e
RD
30import wx
31import wx.xrc
32
2e0ae50e
RD
33#----------------------------------------------------------------------
34
73b2a9a7
RD
35class PythonTemplates:
36 FILE_HEADER = """\
37# This file was automatically generated by pywxrc, do not edit by hand.
2e0ae50e 38
73b2a9a7
RD
39import wx
40import wx.xrc as xrc
2e0ae50e 41
73b2a9a7 42__res = None
2e0ae50e 43
73b2a9a7
RD
44def get_resources():
45 \"\"\" This function provides access to the XML resources in this module.\"\"\"
46 global __res
47 if __res == None:
48 __init_resources()
49 return __res
2e0ae50e 50
73b2a9a7 51"""
2e0ae50e 52
73b2a9a7
RD
53 CLASS_HEADER = """\
54class %(windowName)sBase(wx.%(windowClass)s):
55 def PreCreate(self):
56 \"\"\" This function is called during the class's initialization.
2e0ae50e 57
73b2a9a7
RD
58 Override it for custom setup before the window is created usually to
59 set additional window styles using SetWindowStyle() and SetExtraStyle().\"\"\"
60 pass
2e0ae50e 61
73b2a9a7
RD
62 def __init__(self, parent):
63 # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
64 pre = wx.Pre%(windowClass)s()
65 get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
66 self.PreCreate()
67 self.PostCreate(pre)
2e0ae50e 68
73b2a9a7
RD
69 # Define variables for the controls
70"""
2e0ae50e 71
73b2a9a7
RD
72 CREATE_WIDGET_VAR = """\
73 self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
74"""
2e0ae50e 75
73b2a9a7
RD
76 INIT_RESOURE_HEADER = """\
77# -------------------------------------------------------------
78# ------------------------ Resource data ----------------------
79# -------------------------------------------------------------
2e0ae50e 80
73b2a9a7
RD
81def __init_resources():
82"""
2e0ae50e 83
73b2a9a7
RD
84 LOAD_RES_FILE = """\
85 global __res
86 __res = xrc.XmlResource('%(resourceFilename)s')
87"""
2e0ae50e 88
73b2a9a7
RD
89 FILE_AS_STRING = """\
90 %(filename)s = '''\\
91%(fileData)s'''
2e0ae50e 92
2e0ae50e 93
73b2a9a7 94"""
2e0ae50e 95
73b2a9a7
RD
96 PREPARE_MEMFS = """\
97 # Load all the strings as memory files
98
99 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
100"""
2e0ae50e 101
73b2a9a7
RD
102 ADD_FILE_TO_MEMFS = """\
103 wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
104"""
2e0ae50e 105
73b2a9a7
RD
106 LOAD_RES_MEMFS = """\
107 global __res
108 __res = xrc.EmptyXmlResource()
109 __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
110"""
2e0ae50e 111
73b2a9a7 112#----------------------------------------------------------------------
2e0ae50e 113
73b2a9a7
RD
114class XmlResourceCompiler:
115
116 templates = PythonTemplates()
2e0ae50e 117
73b2a9a7 118 """This class generates Python code from XML resource files (XRC)."""
2e0ae50e 119
73b2a9a7
RD
120 def MakePythonModule(self, resourceFilename, outputFilename, embedResources=False):
121 if outputFilename == "-":
122 outputFile = sys.stdout
2e0ae50e 123 else:
73b2a9a7
RD
124 try:
125 outputFile = open(outputFilename, "wt")
126 except IOError:
127 raise IOError("Can't write output to '%s'" % outputFilename)
2e0ae50e 128
73b2a9a7
RD
129 resourceDocument = minidom.parse(resourceFilename)
130 print >>outputFile, self.templates.FILE_HEADER
131 print >>outputFile, self.GenerateClasses(resourceDocument)
132
133 if embedResources:
134 print >>outputFile, self.GenerateInitResourcesEmbedded(resourceFilename, resourceDocument)
2e0ae50e 135 else:
73b2a9a7 136 print >>outputFile, self.GenerateInitResourcesFile(resourceFilename, resourceDocument)
2e0ae50e 137
73b2a9a7 138 #-------------------------------------------------------------------
2e0ae50e 139
73b2a9a7
RD
140 def GenerateClasses(self, resourceDocument):
141 outputList = []
142
143 resource = resourceDocument.firstChild
144 topWindows = [e for e in resource.childNodes
145 if e.nodeType == e.ELEMENT_NODE and e.tagName == "object"]
146
147 # Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
148 for topWindow in topWindows:
149 windowClass = topWindow.getAttribute("class")
150 windowClass = re.sub("^wx", "", windowClass)
151 windowName = topWindow.getAttribute("name")
152 outputList.append(self.templates.CLASS_HEADER % locals())
2e0ae50e 153
73b2a9a7
RD
154 # Generate a variable for each control, and standard event handlers
155 # for standard controls.
156 for widget in topWindow.getElementsByTagName("object"):
157 widgetClass = widget.getAttribute("class")
158 widgetClass = re.sub("^wx", "", widgetClass)
159 widgetName = widget.getAttribute("name")
160 if (widgetName != "" and widgetClass != "" and
161 widgetClass not in
162 ['tool', 'unknown', 'notebookpage',
163 'separator', 'sizeritem', 'MenuItem']):
164 outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
165 outputList.append('\n\n')
2e0ae50e 166
73b2a9a7 167 return "".join(outputList)
2e0ae50e 168
73b2a9a7 169 #-------------------------------------------------------------------
2e0ae50e 170
73b2a9a7
RD
171 def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
172 outputList = []
2e0ae50e 173
73b2a9a7 174 outputList.append(self.templates.INIT_RESOURE_HEADER)
2e0ae50e 175
73b2a9a7 176 files = []
2e0ae50e 177
73b2a9a7
RD
178 resourcePath = os.path.split(resourceFilename)[0]
179 memoryPath = self.GetMemoryFilename(os.path.splitext(os.path.split(resourceFilename)[1])[0])
180 resourceFilename = self.GetMemoryFilename(os.path.split(resourceFilename)[1])
181
182 self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
183
184 filename = resourceFilename
185 fileData = resourceDocument.toxml()
186 outputList.append(self.templates.FILE_AS_STRING % locals())
2e0ae50e 187
73b2a9a7
RD
188 for f in files:
189 filename = self.GetMemoryFilename(f)
190 fileData = self.FileToString(os.path.join(resourcePath, f))
191 outputList.append(self.templates.FILE_AS_STRING % locals())
2e0ae50e 192
73b2a9a7
RD
193 outputList.append(self.templates.PREPARE_MEMFS % locals())
194
195 for f in [resourceFilename] + files:
196 filename = self.GetMemoryFilename(f)
197 outputList.append(self.templates.ADD_FILE_TO_MEMFS % locals())
198
199 outputList.append(self.templates.LOAD_RES_MEMFS % locals())
2e0ae50e 200
73b2a9a7
RD
201 return "".join(outputList)
202
203 #-------------------------------------------------------------------
2e0ae50e 204
73b2a9a7
RD
205 def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
206 outputList = []
207 outputList.append(self.templates.INIT_RESOURE_HEADER)
208 outputList.append(self.templates.LOAD_RES_FILE % locals())
209 return "".join(outputList)
2e0ae50e 210
73b2a9a7 211 #-------------------------------------------------------------------
2e0ae50e 212
73b2a9a7
RD
213 def GetMemoryFilename(self, filename):
214 # Remove special chars from the filename
215 return re.sub(r"[^A-Za-z0-9_]", "_", filename)
2e0ae50e 216
73b2a9a7 217 #-------------------------------------------------------------------
2e0ae50e 218
73b2a9a7
RD
219 def FileToString(self, filename):
220 outputList = []
221
2e0ae50e 222 buffer = open(filename, "rb").read()
73b2a9a7 223 fileLen = len(buffer)
2e0ae50e
RD
224
225 linelng = 0
73b2a9a7 226 for i in xrange(fileLen):
2e0ae50e
RD
227 s = buffer[i]
228 c = ord(s)
229 if s == '\n':
230 tmp = s
231 linelng = 0
232 elif c < 32 or c > 127 or s == "'":
233 tmp = "\\x%02x" % c
234 elif s == "\\":
235 tmp = "\\\\"
236 else:
237 tmp = s
238
239 if linelng > 70:
240 linelng = 0
73b2a9a7
RD
241 outputList.append("\\\n")
242
243 outputList.append(tmp)
2e0ae50e 244 linelng += len(tmp)
73b2a9a7
RD
245
246 return "".join(outputList)
247
248 #-------------------------------------------------------------------
2e0ae50e 249
73b2a9a7
RD
250 def NodeContainsFilename(self, node):
251 """ Does 'node' contain filename information at all? """
2e0ae50e 252
73b2a9a7
RD
253 # Any bitmaps:
254 if node.nodeName == "bitmap":
255 return True
2e0ae50e 256
73b2a9a7
RD
257 if node.nodeName == "icon":
258 return True
2e0ae50e 259
73b2a9a7
RD
260 # URLs in wxHtmlWindow:
261 if node.nodeName == "url":
262 return True
2e0ae50e 263
73b2a9a7
RD
264 # wxBitmapButton:
265 parent = node.parentNode
266 if parent.__class__ != minidom.Document and \
267 parent.getAttribute("class") == "wxBitmapButton" and \
268 (node.nodeName == "focus" or node.nodeName == "disabled" or
269 node.nodeName == "selected"):
270 return True
2e0ae50e 271
73b2a9a7
RD
272 # wxBitmap or wxIcon toplevel resources:
273 if node.nodeName == "object":
274 klass = node.getAttribute("class")
275 if klass == "wxBitmap" or klass == "wxIcon":
276 return True
2e0ae50e 277
73b2a9a7 278 return False
2e0ae50e 279
73b2a9a7 280 #-------------------------------------------------------------------
2e0ae50e 281
73b2a9a7
RD
282 def ReplaceFilenamesInXRC(self, node, files, resourcePath):
283 """ Finds all files mentioned in resource file, e.g. <bitmap>filename</bitmap>
284 and replaces them with the memory filenames.
285
286 Fills a list of the filenames found."""
2e0ae50e 287
73b2a9a7
RD
288 # Is 'node' XML node element?
289 if node is None: return
290 if node.nodeType != minidom.Document.ELEMENT_NODE: return
2e0ae50e 291
73b2a9a7 292 containsFilename = self.NodeContainsFilename(node);
2e0ae50e 293
73b2a9a7 294 for n in node.childNodes:
2e0ae50e 295
73b2a9a7
RD
296 if (containsFilename and
297 (n.nodeType == minidom.Document.TEXT_NODE or
298 n.nodeType == minidom.Document.CDATA_SECTION_NODE)):
2e0ae50e 299
73b2a9a7
RD
300 filename = n.nodeValue
301 memoryFilename = self.GetMemoryFilename(filename)
302 n.nodeValue = memoryFilename
303
304 if filename not in files:
305 files.append(filename)
306
307 # Recurse into children
308 if n.nodeType == minidom.Document.ELEMENT_NODE:
309 self.ReplaceFilenamesInXRC(n, files, resourcePath);
2e0ae50e
RD
310
311#---------------------------------------------------------------------------
312
73b2a9a7
RD
313def main(args):
314 resourceFilename = ""
315 outputFilename = ""
316 embedResources = False
317
318 try:
319 opts, args = getopt.gnu_getopt(args, "heo:", "help embed output=".split())
320 except getopt.GetoptError:
321 print __doc__
322 sys.exit(1)
2e0ae50e 323
73b2a9a7
RD
324 # If there is no input file argument, show help and exit
325 if args:
326 resourceFilename = args[0]
327 else:
328 print __doc__
329 sys.exit(1)
330
331 # Parse options and arguments
332 for opt, val in opts:
333 if opt in ["-h", "--help"]:
334 print __doc__
335 sys.exit(1)
336
337 if opt in ["-o", "--output"]:
338 outputFilename = val
339
340 if opt in ["-e", "--embed"]:
341 embedResources = True
342
343 if outputFilename is None or outputFilename == "":
344 outputFilename = os.path.splitext(resourceFilename)[0] + "_xrc.py"
345
346 comp = XmlResourceCompiler()
347
348 try:
349 comp.MakePythonModule(resourceFilename, outputFilename, embedResources)
350 except IOError, e:
351 print >>sys.stderr, "%s." % str(e)
352 else:
353 if outputFilename != "-":
354 print >>sys.stderr, "Resources written to %s." % outputFilename
2e0ae50e
RD
355
356if __name__ == "__main__":
73b2a9a7 357 main(sys.argv[1:])
2e0ae50e 358