]>
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 MENU_CLASS_HEADER
= """\
81 class xrc%(windowName)s(wx.%(windowClass)s):
83 pre = get_resources().LoadMenu("%(windowName)s")
85 # This is a copy of Robin's PostCreate voodoo magic in wx.Window that
86 # relinks the self object with the menu object.
88 self.thisown = pre.thisown
90 if hasattr(self, '_setOORInfo'):
91 self._setOORInfo(self)
92 if hasattr(self, '_setCallbackInfo'):
93 self._setCallbackInfo(self, self.__class__)
95 # Define variables for the menu items
98 CREATE_MENUITEM_VAR
= """\
99 self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
102 INIT_RESOURE_HEADER
= """\
103 # ------------------------ Resource data ----------------------
105 def __init_resources():
107 __res = xrc.EmptyXmlResource()
111 __res.Load('%(resourceFilename)s')"""
113 FILE_AS_STRING
= """\
120 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
123 ADD_FILE_TO_MEMFS
= """\
124 wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
127 LOAD_RES_MEMFS
= """\
128 __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
131 GETTEXT_DUMMY_FUNC
= """
132 # ----------------------- Gettext strings ---------------------
134 def __gettext_strings():
135 # This is a dummy function that lists all the strings that are used in
136 # the XRC file in the _("a string") format to be recognized by GNU
137 # gettext utilities (specificaly the xgettext utility) and the
138 # mki18n.py script. For more information see:
139 # http://wiki.wxpython.org/index.cgi/Internationalization
146 #----------------------------------------------------------------------
148 class XmlResourceCompiler
:
150 templates
= PythonTemplates()
152 """This class generates Python code from XML resource files (XRC)."""
154 def MakePythonModule(self
, inputFiles
, outputFilename
,
155 embedResources
=False, generateGetText
=False):
157 outputFile
= self
._OpenOutputFile
(outputFilename
)
163 # process all the inputFiles, collecting the output data
164 for inFile
in inputFiles
:
165 resourceDocument
= minidom
.parse(inFile
)
166 classes
.append(self
.GenerateClasses(resourceDocument
))
169 res
= self
.GenerateInitResourcesEmbedded(inFile
, resourceDocument
)
171 res
= self
.GenerateInitResourcesFile(inFile
, resourceDocument
)
172 resources
.append(res
)
175 gettextStrings
+= self
.FindStringsInNode(resourceDocument
.firstChild
)
177 # now write it all out
178 print >>outputFile
, self
.templates
.FILE_HEADER
180 # Note: Technically it is not legal to have anything other
181 # than ascii for class and variable names, but since the user
182 # can create the XML with non-ascii names we'll go ahead and
183 # allow for it here, and then let Python complain about it
184 # later when they try to run the program.
185 classes
= u
"\n".join(classes
)
186 print >>outputFile
, classes
.encode("UTF-8")
188 print >>outputFile
, self
.templates
.INIT_RESOURE_HEADER
190 print >>outputFile
, self
.templates
.PREPARE_MEMFS
191 resources
= u
"\n".join(resources
)
192 print >>outputFile
, resources
.encode("UTF-8")
195 # These have already been converted to utf-8...
196 gettextStrings
= [' _("%s")' % s
for s
in gettextStrings
]
197 gettextStrings
= "\n".join(gettextStrings
)
198 print >>outputFile
, self
.templates
.GETTEXT_DUMMY_FUNC
% gettextStrings
200 #-------------------------------------------------------------------
202 def MakeGetTextOutput(self
, inputFiles
, outputFilename
):
204 Just output the gettext strings by themselves, with no other
207 outputFile
= self
._OpenOutputFile
(outputFilename
)
208 for inFile
in inputFiles
:
209 resourceDocument
= minidom
.parse(inFile
)
210 resource
= resourceDocument
.firstChild
211 strings
= self
.FindStringsInNode(resource
)
212 strings
= ['_("%s");' % s
for s
in strings
]
213 print >>outputFile
, "\n".join(strings
)
215 #-------------------------------------------------------------------
217 def GenerateClasses(self
, resourceDocument
):
220 resource
= resourceDocument
.firstChild
221 topWindows
= [e
for e
in resource
.childNodes
222 if e
.nodeType
== e
.ELEMENT_NODE
and e
.tagName
== "object"]
224 # Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
225 for topWindow
in topWindows
:
226 windowClass
= topWindow
.getAttribute("class")
227 windowClass
= re
.sub("^wx", "", windowClass
)
228 windowName
= topWindow
.getAttribute("name")
230 if windowClass
== "Menu":
231 outputList
.append(self
.templates
.MENU_CLASS_HEADER
% locals())
233 outputList
.append(self
.templates
.CLASS_HEADER
% locals())
235 # Generate a variable for each control, and standard event handlers
236 # for standard controls.
237 for widget
in topWindow
.getElementsByTagName("object"):
238 widgetClass
= widget
.getAttribute("class")
239 widgetClass
= re
.sub("^wx", "", widgetClass
)
240 widgetName
= widget
.getAttribute("name")
241 if (widgetName
!= "" and widgetClass
!= ""):
242 if widgetClass
== "MenuItem":
243 outputList
.append(self
.templates
.CREATE_MENUITEM_VAR
% locals())
244 elif widgetClass
not in \
245 ['tool', 'unknown', 'notebookpage', 'separator', 'sizeritem']:
246 outputList
.append(self
.templates
.CREATE_WIDGET_VAR
% locals())
247 outputList
.append('\n\n')
249 return "".join(outputList
)
251 #-------------------------------------------------------------------
253 def GenerateInitResourcesEmbedded(self
, resourceFilename
, resourceDocument
):
257 resourcePath
= os
.path
.split(resourceFilename
)[0]
258 memoryPath
= self
.GetMemoryFilename(os
.path
.splitext(os
.path
.split(resourceFilename
)[1])[0])
259 resourceFilename
= self
.GetMemoryFilename(os
.path
.split(resourceFilename
)[1])
261 self
.ReplaceFilenamesInXRC(resourceDocument
.firstChild
, files
, resourcePath
)
263 filename
= resourceFilename
264 fileData
= resourceDocument
.toxml() # what about this? encoding=resourceDocument.encoding)
265 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
268 filename
= self
.GetMemoryFilename(f
)
269 fileData
= self
.FileToString(os
.path
.join(resourcePath
, f
))
270 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
272 for f
in [resourceFilename
] + files
:
273 filename
= self
.GetMemoryFilename(f
)
274 outputList
.append(self
.templates
.ADD_FILE_TO_MEMFS
% locals())
276 outputList
.append(self
.templates
.LOAD_RES_MEMFS
% locals())
278 return "".join(outputList
)
280 #-------------------------------------------------------------------
282 def GenerateInitResourcesFile(self
, resourceFilename
, resourceDocument
):
283 # take only the filename portion out of resourceFilename
284 resourceFilename
= os
.path
.split(resourceFilename
)[1]
286 outputList
.append(self
.templates
.LOAD_RES_FILE
% locals())
287 return "".join(outputList
)
289 #-------------------------------------------------------------------
291 def GetMemoryFilename(self
, filename
):
292 # Remove special chars from the filename
293 return re
.sub(r
"[^A-Za-z0-9_]", "_", filename
)
295 #-------------------------------------------------------------------
297 def FileToString(self
, filename
):
300 buffer = open(filename
, "rb").read()
301 fileLen
= len(buffer)
304 for i
in xrange(fileLen
):
310 elif c
< 32 or c
> 127 or s
== "'":
319 outputList
.append("\\\n")
321 outputList
.append(tmp
)
324 return "".join(outputList
)
326 #-------------------------------------------------------------------
328 def NodeContainsFilename(self
, node
):
329 """ Does 'node' contain filename information at all? """
332 if node
.nodeName
== "bitmap":
335 if node
.nodeName
== "icon":
338 # URLs in wxHtmlWindow:
339 if node
.nodeName
== "url":
343 parent
= node
.parentNode
344 if parent
.__class
__ != minidom
.Document
and \
345 parent
.getAttribute("class") == "wxBitmapButton" and \
346 (node
.nodeName
== "focus" or node
.nodeName
== "disabled" or
347 node
.nodeName
== "selected"):
350 # wxBitmap or wxIcon toplevel resources:
351 if node
.nodeName
== "object":
352 klass
= node
.getAttribute("class")
353 if klass
== "wxBitmap" or klass
== "wxIcon":
358 #-------------------------------------------------------------------
360 def ReplaceFilenamesInXRC(self
, node
, files
, resourcePath
):
361 """ Finds all files mentioned in resource file, e.g. <bitmap>filename</bitmap>
362 and replaces them with the memory filenames.
364 Fills a list of the filenames found."""
366 # Is 'node' XML node element?
367 if node
is None: return
368 if node
.nodeType
!= minidom
.Document
.ELEMENT_NODE
: return
370 containsFilename
= self
.NodeContainsFilename(node
);
372 for n
in node
.childNodes
:
374 if (containsFilename
and
375 (n
.nodeType
== minidom
.Document
.TEXT_NODE
or
376 n
.nodeType
== minidom
.Document
.CDATA_SECTION_NODE
)):
378 filename
= n
.nodeValue
379 memoryFilename
= self
.GetMemoryFilename(filename
)
380 n
.nodeValue
= memoryFilename
382 if filename
not in files
:
383 files
.append(filename
)
385 # Recurse into children
386 if n
.nodeType
== minidom
.Document
.ELEMENT_NODE
:
387 self
.ReplaceFilenamesInXRC(n
, files
, resourcePath
);
389 #-------------------------------------------------------------------
391 def FindStringsInNode(self
, parent
):
403 for child
in parent
.childNodes
:
404 if ((parent
.nodeType
== parent
.ELEMENT_NODE
) and
405 # parent is an element, i.e. has subnodes...
406 (child
.nodeType
== child
.TEXT_NODE
or
407 child
.nodeType
== child
.CDATA_SECTION_NODE
) and
408 # ...it is textnode...
410 parent
.tagName
== "label" or
411 (parent
.tagName
== "value" and
412 not is_number(child
.nodeValue
)) or
413 parent
.tagName
== "help" or
414 parent
.tagName
== "longhelp" or
415 parent
.tagName
== "tooltip" or
416 parent
.tagName
== "htmlcode" or
417 parent
.tagName
== "title" or
418 parent
.tagName
== "item"
420 # ...and known to contain translatable string
421 if (parent
.getAttribute("translate") != "0"):
422 strings
.append(self
.ConvertText(child
.nodeValue
))
425 if child
.nodeType
== child
.ELEMENT_NODE
:
426 strings
+= self
.FindStringsInNode(child
)
430 #-------------------------------------------------------------------
432 def ConvertText(self
, st
):
437 for i
in range(len(dt
)):
455 if dt
[i
+1] not in ['n', 't', 'r']:
464 return st2
.encode("UTF-8")
467 #-------------------------------------------------------------------
469 def _OpenOutputFile(self
, outputFilename
):
470 if outputFilename
== "-":
471 outputFile
= sys
.stdout
474 outputFile
= open(outputFilename
, "wt")
476 raise IOError("Can't write output to '%s'" % outputFilename
)
483 #---------------------------------------------------------------------------
486 resourceFilename
= ""
487 outputFilename
= None
488 embedResources
= False
489 generateGetText
= False
490 generatePython
= False
493 opts
, args
= getopt
.gnu_getopt(args
,
495 "help python gettext embed output=".split())
496 except getopt
.GetoptError
, e
:
497 print "\nError : %s\n" % str(e
)
501 # If there is no input file argument, show help and exit
504 print "No xrc input file was specified."
507 # Parse options and arguments
508 for opt
, val
in opts
:
509 if opt
in ["-h", "--help"]:
513 if opt
in ["-p", "--python"]:
514 generatePython
= True
516 if opt
in ["-o", "--output"]:
519 if opt
in ["-e", "--embed"]:
520 embedResources
= True
522 if opt
in ["-g", "--gettext"]:
523 generateGetText
= True
526 # check for and expand any wildcards in the list of input files
529 inputFiles
+= glob
.glob(arg
)
532 comp
= XmlResourceCompiler()
536 if not outputFilename
:
537 outputFilename
= os
.path
.splitext(args
[0])[0] + "_xrc.py"
538 comp
.MakePythonModule(inputFiles
, outputFilename
,
539 embedResources
, generateGetText
)
541 elif generateGetText
:
542 if not outputFilename
:
544 comp
.MakeGetTextOutput(inputFiles
, outputFilename
)
548 print "One or both of -p, -g must be specified."
553 print >>sys
.stderr
, "%s." % str(e
)
555 if outputFilename
!= "-":
556 print >>sys
.stderr
, "Resources written to %s." % outputFilename
558 if __name__
== "__main__":