]>
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):
59 def PreCreate(self, pre):
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()
70 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)
93 # Define variables for the menu items
96 MENUBAR_CLASS_HEADER
= """\
97 class xrc%(windowName)s(wx.%(windowClass)s):
99 pre = get_resources().LoadMenuBar("%(windowName)s")
102 # Define variables for the menu items
105 CREATE_MENUITEM_VAR
= """\
106 self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
109 INIT_RESOURE_HEADER
= """\
110 # ------------------------ Resource data ----------------------
112 def __init_resources():
114 __res = xrc.EmptyXmlResource()
118 __res.Load('%(resourceFilename)s')"""
120 FILE_AS_STRING
= """\
127 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
130 ADD_FILE_TO_MEMFS
= """\
131 wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
134 LOAD_RES_MEMFS
= """\
135 __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
138 GETTEXT_DUMMY_FUNC
= """
139 # ----------------------- Gettext strings ---------------------
141 def __gettext_strings():
142 # This is a dummy function that lists all the strings that are used in
143 # the XRC file in the _("a string") format to be recognized by GNU
144 # gettext utilities (specificaly the xgettext utility) and the
145 # mki18n.py script. For more information see:
146 # http://wiki.wxpython.org/index.cgi/Internationalization
153 #----------------------------------------------------------------------
155 class XmlResourceCompiler
:
157 templates
= PythonTemplates()
159 """This class generates Python code from XML resource files (XRC)."""
161 def MakePythonModule(self
, inputFiles
, outputFilename
,
162 embedResources
=False, generateGetText
=False):
164 outputFile
= self
._OpenOutputFile
(outputFilename
)
170 # process all the inputFiles, collecting the output data
171 for inFile
in inputFiles
:
172 resourceDocument
= minidom
.parse(inFile
)
173 classes
.append(self
.GenerateClasses(resourceDocument
))
176 res
= self
.GenerateInitResourcesEmbedded(inFile
, resourceDocument
)
178 res
= self
.GenerateInitResourcesFile(inFile
, resourceDocument
)
179 resources
.append(res
)
182 gettextStrings
+= self
.FindStringsInNode(resourceDocument
.firstChild
)
184 # now write it all out
185 print >>outputFile
, self
.templates
.FILE_HEADER
187 # Note: Technically it is not legal to have anything other
188 # than ascii for class and variable names, but since the user
189 # can create the XML with non-ascii names we'll go ahead and
190 # allow for it here, and then let Python complain about it
191 # later when they try to run the program.
192 classes
= u
"\n".join(classes
)
193 print >>outputFile
, classes
.encode("UTF-8")
195 print >>outputFile
, self
.templates
.INIT_RESOURE_HEADER
197 print >>outputFile
, self
.templates
.PREPARE_MEMFS
198 resources
= u
"\n".join(resources
)
199 print >>outputFile
, resources
.encode("UTF-8")
202 # These have already been converted to utf-8...
203 gettextStrings
= [' _("%s")' % s
for s
in gettextStrings
]
204 gettextStrings
= "\n".join(gettextStrings
)
205 print >>outputFile
, self
.templates
.GETTEXT_DUMMY_FUNC
% gettextStrings
207 #-------------------------------------------------------------------
209 def MakeGetTextOutput(self
, inputFiles
, outputFilename
):
211 Just output the gettext strings by themselves, with no other
214 outputFile
= self
._OpenOutputFile
(outputFilename
)
215 for inFile
in inputFiles
:
216 resourceDocument
= minidom
.parse(inFile
)
217 resource
= resourceDocument
.firstChild
218 strings
= self
.FindStringsInNode(resource
)
219 strings
= ['_("%s");' % s
for s
in strings
]
220 print >>outputFile
, "\n".join(strings
)
222 #-------------------------------------------------------------------
224 def GenerateClasses(self
, resourceDocument
):
227 resource
= resourceDocument
.firstChild
228 topWindows
= [e
for e
in resource
.childNodes
229 if e
.nodeType
== e
.ELEMENT_NODE
and e
.tagName
== "object"]
231 # Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
232 for topWindow
in topWindows
:
233 windowClass
= topWindow
.getAttribute("class")
234 windowClass
= re
.sub("^wx", "", windowClass
)
235 windowName
= topWindow
.getAttribute("name")
237 if windowClass
in ["MenuBar"]:
238 outputList
.append(self
.templates
.MENUBAR_CLASS_HEADER
% locals())
239 elif windowClass
in ["Menu"]:
240 outputList
.append(self
.templates
.MENU_CLASS_HEADER
% locals())
242 outputList
.append(self
.templates
.CLASS_HEADER
% locals())
244 # Generate a variable for each control, and standard event handlers
245 # for standard controls.
246 for widget
in topWindow
.getElementsByTagName("object"):
247 widgetClass
= widget
.getAttribute("class")
248 widgetClass
= re
.sub("^wx", "", widgetClass
)
249 widgetName
= widget
.getAttribute("name")
250 if (widgetName
!= "" and widgetClass
!= ""):
251 if widgetClass
== "MenuItem":
252 outputList
.append(self
.templates
.CREATE_MENUITEM_VAR
% locals())
253 elif widgetClass
not in \
254 ['tool', 'unknown', 'notebookpage', 'separator', 'sizeritem']:
255 outputList
.append(self
.templates
.CREATE_WIDGET_VAR
% locals())
256 outputList
.append('\n\n')
258 return "".join(outputList
)
260 #-------------------------------------------------------------------
262 def GenerateInitResourcesEmbedded(self
, resourceFilename
, resourceDocument
):
266 resourcePath
= os
.path
.split(resourceFilename
)[0]
267 memoryPath
= self
.GetMemoryFilename(os
.path
.splitext(os
.path
.split(resourceFilename
)[1])[0])
268 resourceFilename
= self
.GetMemoryFilename(os
.path
.split(resourceFilename
)[1])
270 self
.ReplaceFilenamesInXRC(resourceDocument
.firstChild
, files
, resourcePath
)
272 filename
= resourceFilename
273 fileData
= resourceDocument
.toxml() # what about this? encoding=resourceDocument.encoding)
274 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
277 filename
= self
.GetMemoryFilename(f
)
278 fileData
= self
.FileToString(os
.path
.join(resourcePath
, f
))
279 outputList
.append(self
.templates
.FILE_AS_STRING
% locals())
281 for f
in [resourceFilename
] + files
:
282 filename
= self
.GetMemoryFilename(f
)
283 outputList
.append(self
.templates
.ADD_FILE_TO_MEMFS
% locals())
285 outputList
.append(self
.templates
.LOAD_RES_MEMFS
% locals())
287 return "".join(outputList
)
289 #-------------------------------------------------------------------
291 def GenerateInitResourcesFile(self
, resourceFilename
, resourceDocument
):
292 # take only the filename portion out of resourceFilename
293 resourceFilename
= os
.path
.split(resourceFilename
)[1]
295 outputList
.append(self
.templates
.LOAD_RES_FILE
% locals())
296 return "".join(outputList
)
298 #-------------------------------------------------------------------
300 def GetMemoryFilename(self
, filename
):
301 # Remove special chars from the filename
302 return re
.sub(r
"[^A-Za-z0-9_]", "_", filename
)
304 #-------------------------------------------------------------------
306 def FileToString(self
, filename
):
309 buffer = open(filename
, "rb").read()
310 fileLen
= len(buffer)
313 for i
in xrange(fileLen
):
319 elif c
< 32 or c
> 127 or s
== "'":
328 outputList
.append("\\\n")
330 outputList
.append(tmp
)
333 return "".join(outputList
)
335 #-------------------------------------------------------------------
337 def NodeContainsFilename(self
, node
):
338 """ Does 'node' contain filename information at all? """
341 if node
.nodeName
== "bitmap":
344 if node
.nodeName
== "icon":
347 # URLs in wxHtmlWindow:
348 if node
.nodeName
== "url":
352 parent
= node
.parentNode
353 if parent
.__class
__ != minidom
.Document
and \
354 parent
.getAttribute("class") == "wxBitmapButton" and \
355 (node
.nodeName
== "focus" or node
.nodeName
== "disabled" or
356 node
.nodeName
== "selected"):
359 # wxBitmap or wxIcon toplevel resources:
360 if node
.nodeName
== "object":
361 klass
= node
.getAttribute("class")
362 if klass
== "wxBitmap" or klass
== "wxIcon":
367 #-------------------------------------------------------------------
369 def ReplaceFilenamesInXRC(self
, node
, files
, resourcePath
):
370 """ Finds all files mentioned in resource file, e.g. <bitmap>filename</bitmap>
371 and replaces them with the memory filenames.
373 Fills a list of the filenames found."""
375 # Is 'node' XML node element?
376 if node
is None: return
377 if node
.nodeType
!= minidom
.Document
.ELEMENT_NODE
: return
379 containsFilename
= self
.NodeContainsFilename(node
);
381 for n
in node
.childNodes
:
383 if (containsFilename
and
384 (n
.nodeType
== minidom
.Document
.TEXT_NODE
or
385 n
.nodeType
== minidom
.Document
.CDATA_SECTION_NODE
)):
387 filename
= n
.nodeValue
388 memoryFilename
= self
.GetMemoryFilename(filename
)
389 n
.nodeValue
= memoryFilename
391 if filename
not in files
:
392 files
.append(filename
)
394 # Recurse into children
395 if n
.nodeType
== minidom
.Document
.ELEMENT_NODE
:
396 self
.ReplaceFilenamesInXRC(n
, files
, resourcePath
);
398 #-------------------------------------------------------------------
400 def FindStringsInNode(self
, parent
):
412 for child
in parent
.childNodes
:
413 if ((parent
.nodeType
== parent
.ELEMENT_NODE
) and
414 # parent is an element, i.e. has subnodes...
415 (child
.nodeType
== child
.TEXT_NODE
or
416 child
.nodeType
== child
.CDATA_SECTION_NODE
) and
417 # ...it is textnode...
419 parent
.tagName
== "label" or
420 (parent
.tagName
== "value" and
421 not is_number(child
.nodeValue
)) or
422 parent
.tagName
== "help" or
423 parent
.tagName
== "longhelp" or
424 parent
.tagName
== "tooltip" or
425 parent
.tagName
== "htmlcode" or
426 parent
.tagName
== "title" or
427 parent
.tagName
== "item"
429 # ...and known to contain translatable string
430 if (parent
.getAttribute("translate") != "0"):
431 strings
.append(self
.ConvertText(child
.nodeValue
))
434 if child
.nodeType
== child
.ELEMENT_NODE
:
435 strings
+= self
.FindStringsInNode(child
)
439 #-------------------------------------------------------------------
441 def ConvertText(self
, st
):
446 for i
in range(len(dt
)):
464 if dt
[i
+1] not in ['n', 't', 'r']:
473 return st2
.encode("UTF-8")
476 #-------------------------------------------------------------------
478 def _OpenOutputFile(self
, outputFilename
):
479 if outputFilename
== "-":
480 outputFile
= sys
.stdout
483 outputFile
= open(outputFilename
, "wt")
485 raise IOError("Can't write output to '%s'" % outputFilename
)
492 #---------------------------------------------------------------------------
495 resourceFilename
= ""
496 outputFilename
= None
497 embedResources
= False
498 generateGetText
= False
499 generatePython
= False
502 opts
, args
= getopt
.gnu_getopt(args
,
504 "help python gettext embed output=".split())
505 except getopt
.GetoptError
, e
:
506 print "\nError : %s\n" % str(e
)
510 # If there is no input file argument, show help and exit
513 print "No xrc input file was specified."
516 # Parse options and arguments
517 for opt
, val
in opts
:
518 if opt
in ["-h", "--help"]:
522 if opt
in ["-p", "--python"]:
523 generatePython
= True
525 if opt
in ["-o", "--output"]:
528 if opt
in ["-e", "--embed"]:
529 embedResources
= True
531 if opt
in ["-g", "--gettext"]:
532 generateGetText
= True
535 # check for and expand any wildcards in the list of input files
538 inputFiles
+= glob
.glob(arg
)
541 comp
= XmlResourceCompiler()
545 if not outputFilename
:
546 outputFilename
= os
.path
.splitext(args
[0])[0] + "_xrc.py"
547 comp
.MakePythonModule(inputFiles
, outputFilename
,
548 embedResources
, generateGetText
)
550 elif generateGetText
:
551 if not outputFilename
:
553 comp
.MakeGetTextOutput(inputFiles
, outputFilename
)
557 print "One or both of -p, -g must be specified."
562 print >>sys
.stderr
, "%s." % str(e
)
564 if outputFilename
!= "-":
565 print >>sys
.stderr
, "Resources written to %s." % outputFilename
567 if __name__
== "__main__":