]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/pywxrc.py
1 #----------------------------------------------------------------------
2 # Name: wx.tools.pywxrc
3 # Purpose: XML resource compiler
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
10 # Massive rework by Eli Golovinsky
13 # Copyright: (c) 2004 by Total Control Software, 2000 Vaclav Slavik
14 # Licence: wxWindows license
15 #----------------------------------------------------------------------
18 pywxrc -- Python XML resource compiler
19 (see http://wiki.wxpython.org/index.cgi/pywxrc for more info)
21 Usage: python pywxrc.py -h
22 python pywxrc.py [-p] [-g] [-e] [-o filename] xrc input files...
24 -h, --help show help message
25 -p, --python generate python module
26 -g, --gettext output list of translatable strings (may be combined with -p)
27 -e, --embed embed XRC resources in the output file
28 -o, --output output filename, or - for stdout
31 import sys
, os
, getopt
, glob
, re
32 import xml
.dom
.minidom
as minidom
36 #----------------------------------------------------------------------
38 class PythonTemplates
:
40 # This file was automatically generated by pywxrc, do not edit by hand.
41 # -*- coding: UTF-8 -*-
49 \"\"\" This function provides access to the XML resources in this module.\"\"\"
58 class xrc%(windowName)s(wx.%(windowClass)s):
60 \"\"\" This function is called during the class's initialization.
62 Override it for custom setup before the window is created usually to
63 set additional window styles using SetWindowStyle() and SetExtraStyle().\"\"\"
66 def __init__(self, parent):
67 # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
68 pre = wx.Pre%(windowClass)s()
69 get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
73 # Define variables for the controls
76 CREATE_WIDGET_VAR
= """\
77 self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
80 INIT_RESOURE_HEADER
= """\
81 # ------------------------ Resource data ----------------------
83 def __init_resources():
85 __res = xrc.EmptyXmlResource()
89 __res.Load('%(resourceFilename)s')"""
98 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
101 ADD_FILE_TO_MEMFS
= """\
102 wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
105 LOAD_RES_MEMFS
= """\
106 __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
109 GETTEXT_DUMMY_FUNC
= """
110 # ----------------------- Gettext strings ---------------------
112 def __gettext_strings():
113 # This is a dummy function that lists all the strings that are used in
114 # the XRC file in the _("a string") format to be recognized by GNU
115 # gettext utilities (specificaly the xgettext utility) and the
116 # mki18n.py script. For more information see:
117 # http://wiki.wxpython.org/index.cgi/Internationalization
124 #----------------------------------------------------------------------
126 class XmlResourceCompiler
:
128 templates
= PythonTemplates()
130 """This class generates Python code from XML resource files (XRC)."""
132 def MakePythonModule(self
, inputFiles
, outputFilename
,
133 embedResources
=False, generateGetText
=False):
135 outputFile
= self
._OpenOutputFile
(outputFilename
)
141 # process all the inputFiles, collecting the output data
142 for inFile
in inputFiles
:
143 resourceDocument
= minidom
.parse(inFile
)
144 classes
.append(self
.GenerateClasses(resourceDocument
))
147 res
= self
.GenerateInitResourcesEmbedded(inFile
, resourceDocument
)
149 res
= self
.GenerateInitResourcesFile(inFile
, resourceDocument
)
150 resources
.append(res
)
153 gettextStrings
+= self
.FindStringsInNode(resourceDocument
.firstChild
)
155 # now write it all out
156 print >>outputFile
, self
.templates
.FILE_HEADER
158 # Note: Technically it is not legal to have anything other
159 # than ascii for class and variable names, but since the user
160 # can create the XML with non-ascii names we'll go ahead and
161 # allow for it here, and then let Python complain about it
162 # later when they try to run the program.
163 classes
= u
"\n".join(classes
)
164 print >>outputFile
, classes
.encode("UTF-8")
166 print >>outputFile
, self
.templates
.INIT_RESOURE_HEADER
168 print >>outputFile
, self
.templates
.PREPARE_MEMFS
169 resources
= u
"\n".join(resources
)
170 print >>outputFile
, resources
.encode("UTF-8")
173 # These have already been converted to utf-8...
174 gettextStrings
= [' _("%s")' % s
for s
in gettextStrings
]
175 gettextStrings
= "\n".join(gettextStrings
)
176 print >>outputFile
, self
.templates
.GETTEXT_DUMMY_FUNC
% gettextStrings
178 #-------------------------------------------------------------------
180 def MakeGetTextOutput(self
, inputFiles
, outputFilename
):
182 Just output the gettext strings by themselves, with no other
185 outputFile
= self
._OpenOutputFile
(outputFilename
)
186 for inFile
in inputFiles
:
187 resourceDocument
= minidom
.parse(inFile
)
188 resource
= resourceDocument
.firstChild
189 strings
= self
.FindStringsInNode(resource
)
190 strings
= ['_("%s");' % s
for s
in strings
]
191 print >>outputFile
, "\n".join(strings
)
193 #-------------------------------------------------------------------
195 def GenerateClasses(self
, resourceDocument
):
198 resource
= resourceDocument
.firstChild
199 topWindows
= [e
for e
in resource
.childNodes
200 if e
.nodeType
== e
.ELEMENT_NODE
and e
.tagName
== "object"]
202 # Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
203 for topWindow
in topWindows
:
204 windowClass
= topWindow
.getAttribute("class")
205 windowClass
= re
.sub("^wx", "", windowClass
)
206 windowName
= topWindow
.getAttribute("name")
207 outputList
.append(self
.templates
.CLASS_HEADER
% locals())
209 # Generate a variable for each control, and standard event handlers
210 # for standard controls.
211 for widget
in topWindow
.getElementsByTagName("object"):
212 widgetClass
= widget
.getAttribute("class")
213 widgetClass
= re
.sub("^wx", "", widgetClass
)
214 widgetName
= widget
.getAttribute("name")
215 if (widgetName
!= "" and widgetClass
!= "" and
217 ['tool', 'unknown', 'notebookpage',
218 'separator', 'sizeritem', 'MenuItem']):
219 outputList
.append(self
.templates
.CREATE_WIDGET_VAR
% locals())
220 outputList
.append('\n\n')
222 return "".join(outputList
)
224 #-------------------------------------------------------------------
226 def GenerateInitResourcesEmbedded(self
, resourceFilename
, resourceDocument
):
230 resourcePath
= os
.path
.split(resourceFilename
)[0]
231 memoryPath
= self
.GetMemoryFilename(os
.path
.splitext(os
.path
.split(resourceFilename
)[1])[0])
232 resourceFilename
= self
.GetMemoryFilename(os
.path
.split(resourceFilename
)[1])
234 self
.ReplaceFilenamesInXRC(resourceDocument
.firstChild
, files
, resourcePath
)
236 filename
= resourceFilename
237 fileData
= resourceDocument
.toxml()
238 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
241 filename
= self
.GetMemoryFilename(f
)
242 fileData
= self
.FileToString(os
.path
.join(resourcePath
, f
))
243 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
245 for f
in [resourceFilename
] + files
:
246 filename
= self
.GetMemoryFilename(f
)
247 outputList
.append(self
.templates
.ADD_FILE_TO_MEMFS
% locals())
249 outputList
.append(self
.templates
.LOAD_RES_MEMFS
% locals())
251 return "".join(outputList
)
253 #-------------------------------------------------------------------
255 def GenerateInitResourcesFile(self
, resourceFilename
, resourceDocument
):
256 # take only the filename portion out of resourceFilename
257 resourceFilename
= os
.path
.split(resourceFilename
)[1]
259 outputList
.append(self
.templates
.LOAD_RES_FILE
% locals())
260 return "".join(outputList
)
262 #-------------------------------------------------------------------
264 def GetMemoryFilename(self
, filename
):
265 # Remove special chars from the filename
266 return re
.sub(r
"[^A-Za-z0-9_]", "_", filename
)
268 #-------------------------------------------------------------------
270 def FileToString(self
, filename
):
273 buffer = open(filename
, "rb").read()
274 fileLen
= len(buffer)
277 for i
in xrange(fileLen
):
283 elif c
< 32 or c
> 127 or s
== "'":
292 outputList
.append("\\\n")
294 outputList
.append(tmp
)
297 return "".join(outputList
)
299 #-------------------------------------------------------------------
301 def NodeContainsFilename(self
, node
):
302 """ Does 'node' contain filename information at all? """
305 if node
.nodeName
== "bitmap":
308 if node
.nodeName
== "icon":
311 # URLs in wxHtmlWindow:
312 if node
.nodeName
== "url":
316 parent
= node
.parentNode
317 if parent
.__class
__ != minidom
.Document
and \
318 parent
.getAttribute("class") == "wxBitmapButton" and \
319 (node
.nodeName
== "focus" or node
.nodeName
== "disabled" or
320 node
.nodeName
== "selected"):
323 # wxBitmap or wxIcon toplevel resources:
324 if node
.nodeName
== "object":
325 klass
= node
.getAttribute("class")
326 if klass
== "wxBitmap" or klass
== "wxIcon":
331 #-------------------------------------------------------------------
333 def ReplaceFilenamesInXRC(self
, node
, files
, resourcePath
):
334 """ Finds all files mentioned in resource file, e.g. <bitmap>filename</bitmap>
335 and replaces them with the memory filenames.
337 Fills a list of the filenames found."""
339 # Is 'node' XML node element?
340 if node
is None: return
341 if node
.nodeType
!= minidom
.Document
.ELEMENT_NODE
: return
343 containsFilename
= self
.NodeContainsFilename(node
);
345 for n
in node
.childNodes
:
347 if (containsFilename
and
348 (n
.nodeType
== minidom
.Document
.TEXT_NODE
or
349 n
.nodeType
== minidom
.Document
.CDATA_SECTION_NODE
)):
351 filename
= n
.nodeValue
352 memoryFilename
= self
.GetMemoryFilename(filename
)
353 n
.nodeValue
= memoryFilename
355 if filename
not in files
:
356 files
.append(filename
)
358 # Recurse into children
359 if n
.nodeType
== minidom
.Document
.ELEMENT_NODE
:
360 self
.ReplaceFilenamesInXRC(n
, files
, resourcePath
);
362 #-------------------------------------------------------------------
364 def FindStringsInNode(self
, parent
):
376 for child
in parent
.childNodes
:
377 if ((parent
.nodeType
== parent
.ELEMENT_NODE
) and
378 # parent is an element, i.e. has subnodes...
379 (child
.nodeType
== child
.TEXT_NODE
or
380 child
.nodeType
== child
.CDATA_SECTION_NODE
) and
381 # ...it is textnode...
383 parent
.tagName
== "label" or
384 (parent
.tagName
== "value" and
385 not is_number(child
.nodeValue
)) or
386 parent
.tagName
== "help" or
387 parent
.tagName
== "longhelp" or
388 parent
.tagName
== "tooltip" or
389 parent
.tagName
== "htmlcode" or
390 parent
.tagName
== "title" or
391 parent
.tagName
== "item"
393 # ...and known to contain translatable string
394 if (parent
.getAttribute("translate") != "0"):
395 strings
.append(self
.ConvertText(child
.nodeValue
))
398 if child
.nodeType
== child
.ELEMENT_NODE
:
399 strings
+= self
.FindStringsInNode(child
)
403 #-------------------------------------------------------------------
405 def ConvertText(self
, st
):
410 for i
in range(len(dt
)):
428 if dt
[i
+1] not in ['n', 't', 'r']:
437 return st2
.encode("UTF-8")
440 #-------------------------------------------------------------------
442 def _OpenOutputFile(self
, outputFilename
):
443 if outputFilename
== "-":
444 outputFile
= sys
.stdout
447 outputFile
= open(outputFilename
, "wt")
449 raise IOError("Can't write output to '%s'" % outputFilename
)
456 #---------------------------------------------------------------------------
459 resourceFilename
= ""
460 outputFilename
= None
461 embedResources
= False
462 generateGetText
= False
463 generatePython
= False
466 opts
, args
= getopt
.gnu_getopt(args
,
468 "help python gettext embed output=".split())
469 except getopt
.GetoptError
, e
:
470 print "\nError : %s\n" % str(e
)
474 # If there is no input file argument, show help and exit
477 print "No xrc input file was specified."
480 # Parse options and arguments
481 for opt
, val
in opts
:
482 if opt
in ["-h", "--help"]:
486 if opt
in ["-p", "--python"]:
487 generatePython
= True
489 if opt
in ["-o", "--output"]:
492 if opt
in ["-e", "--embed"]:
493 embedResources
= True
495 if opt
in ["-g", "--gettext"]:
496 generateGetText
= True
499 # check for and expand any wildcards in the list of input files
502 inputFiles
+= glob
.glob(arg
)
505 comp
= XmlResourceCompiler()
509 if not outputFilename
:
510 outputFilename
= os
.path
.splitext(args
[0])[0] + "_xrc.py"
511 comp
.MakePythonModule(inputFiles
, outputFilename
,
512 embedResources
, generateGetText
)
514 elif generateGetText
:
515 if not outputFilename
:
517 comp
.MakeGetTextOutput(inputFiles
, outputFilename
)
521 print "One or both of -p, -g must be specified."
526 print >>sys
.stderr
, "%s." % str(e
)
528 if outputFilename
!= "-":
529 print >>sys
.stderr
, "Resources written to %s." % outputFilename
531 if __name__
== "__main__":