]>
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__":