Thread: [pywin32-checkins] pywin32/win32/Lib win32rcparser.py,NONE,1.1
OLD project page for the Python extensions for Windows
Brought to you by:
mhammond
From: Mark H. <mha...@us...> - 2004-04-09 11:26:39
|
Update of /cvsroot/pywin32/pywin32/win32/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26588/Lib Added Files: win32rcparser.py Log Message: New win32rcparser module for parsing Windows .rc files, and demos and tests --- NEW FILE: win32rcparser.py --- # Windows dialog .RC file parser, by Adam Walker. # This module was adapted from the spambayes project, and is Copyright # 2003/2004 The Python Software Foundation and is covered by the Python # Software Foundation license. """ This is a parser for Windows .rc files, which are text files which define dialogs and other Windows UI resources. """ __author__="Adam Walker" import sys, os, shlex import win32con import commctrl _controlMap = {"DEFPUSHBUTTON":0x80, "PUSHBUTTON":0x80, "Button":0x80, "GROUPBOX":0x80, "Static":0x82, "CTEXT":0x82, "RTEXT":0x82, "LTEXT":0x82, "LISTBOX":0x83, "SCROLLBAR":0x84, "COMBOBOX":0x85, "EDITTEXT":0x81, } _addDefaults = {"EDITTEXT":win32con.WS_BORDER, "GROUPBOX":win32con.BS_GROUPBOX, "LTEXT":win32con.SS_LEFT, "DEFPUSHBUTTON":win32con.BS_DEFPUSHBUTTON, "CTEXT":win32con.SS_CENTER, "RTEXT":win32con.SS_RIGHT} defaultControlStyle = win32con.WS_CHILD | win32con.WS_VISIBLE class DialogDef: name = "" id = 0 style = 0 styleEx = None caption = "" font = "MS Sans Serif" fontSize = 8 x = 0 y = 0 w = 0 h = 0 template = None def __init__(self, n, i): self.name = n self.id = i self.styles = [] self.stylesEx = [] self.controls = [] #print "dialog def for ",self.name, self.id def createDialogTemplate(self): t = None self.template = [[self.caption, (self.x,self.y,self.w,self.h), self.style, self.styleEx, (self.fontSize, self.font)] ] # Add the controls for control in self.controls: self.template.append(control.createDialogTemplate()) return self.template class ControlDef: id = "" controlType = "" subType = "" idNum = 0 style = defaultControlStyle label = "" x = 0 y = 0 w = 0 h = 0 def __init__(self): self.styles = [] def toString(self): s = "<Control id:"+self.id+" controlType:"+self.controlType+" subType:"+self.subType\ +" idNum:"+str(self.idNum)+" style:"+str(self.style)+" styles:"+str(self.styles)+" label:"+self.label\ +" x:"+str(self.x)+" y:"+str(self.y)+" w:"+str(self.w)+" h:"+str(self.h)+">" return s def createDialogTemplate(self): ct = self.controlType if "CONTROL"==ct: ct = self.subType if ct in _addDefaults: self.style |= _addDefaults[ct] if ct in _controlMap: ct = _controlMap[ct] t = [ct, self.label, self.idNum, (self.x, self.y, self.w, self.h), self.style] #print t return t class StringDef: def __init__(self, id, idNum, value): self.id = id self.idNum = idNum self.value = value class RCParser: next_id = 1001 dialogs = {} _dialogs = {} debugEnabled = False token = "" def __init__(self): self.ids = {"IDC_STATIC": -1} self.names = {-1:"IDC_STATIC"} self.bitmaps = {} self.stringTable = {} self.icons = {} def debug(self, *args): if self.debugEnabled: print args def getToken(self): self.token = self.lex.get_token() self.debug("getToken returns:", self.token) if self.token=="": self.token = None return self.token def getCheckToken(self, expected): tok = self.getToken() assert tok == expected, "Expected token '%s', but got token '%s'!" % (expected, tok) return tok def getCommaToken(self): return self.getCheckToken(",") def load(self, rcstream): """ RCParser.loadDialogs(rcFileName) -> None Load the dialog information into the parser. Dialog Definations can then be accessed using the "dialogs" dictionary member (name->DialogDef). The "ids" member contains the dictionary of id->name. The "names" member contains the dictionary of name->id """ self.open(rcstream) self.getToken() while self.token!=None: self.parse() self.getToken() def open(self, rcstream): self.lex = shlex.shlex(rcstream) self.lex.commenters = "//#" def parseH(self, file): lex = shlex.shlex(file) lex.commenters = "//" token = " " while token is not None: token = lex.get_token() if token == "" or token is None: token = None else: if token=='define': n = lex.get_token() i = int(lex.get_token()) self.ids[n] = i if self.names.has_key(i): # ignore AppStudio special ones if not n.startswith("_APS_"): print "Duplicate id",i,"for",n,"is", self.names[i] else: self.names[i] = n if self.next_id<=i: self.next_id = i+1 def parse(self): noid_parsers = { "STRINGTABLE": self.parse_stringtable, } id_parsers = { "DIALOG" : self.parse_dialog, "DIALOGEX": self.parse_dialog, # "TEXTINCLUDE": self.parse_textinclude, "BITMAP": self.parse_bitmap, "ICON": self.parse_icon, } deep = 0 base_token = self.token rp = noid_parsers.get(base_token) if rp is not None: rp() else: # Not something we parse that isn't prefixed by an ID # See if it is an ID prefixed item - if it is, our token # is the resource ID. resource_id = self.token self.getToken() if self.token is None: return if "BEGIN" == self.token: # A 'BEGIN' for a structure we don't understand - skip to the # matching 'END' deep = 1 while deep!=0 and self.token is not None: self.getToken() self.debug("Zooming over", self.token) if "BEGIN" == self.token: deep += 1 elif "END" == self.token: deep -= 1 else: rp = id_parsers.get(self.token) if rp is not None: self.debug("Dispatching '%s'" % (self.token,)) rp(resource_id) else: self.debug("Skipping top-level '%s'" % base_token) def addId(self, id_name): if id_name in self.ids: id = self.ids[id_name] else: # IDOK and IDCANCEL are special - if a real resource has a value # of OK or CANCEL (1 or 2), we still want the user's name. if id_name == "IDOK": self.ids[id_name] = win32con.IDOK return win32con.IDOK if id_name == "IDCANCEL": self.ids[id_name] = win32con.IDCANCEL return win32con.IDCANCEL id = self.next_id self.next_id += 1 self.ids[id_name] = id self.names[id] = id_name return id def lang(self): while self.token[0:4]=="LANG" or self.token[0:7]=="SUBLANG" or self.token==',': self.getToken(); def parse_textinclude(self, res_id): while self.getToken() != "BEGIN": pass while 1: if self.token == "END": break s = self.getToken() def parse_stringtable(self): while self.getToken() != "BEGIN": pass self.getToken() while 1: if self.token == "END": break sid = self.token # Handle quoted strings - pity shlex doesn't handle it. bits = [self.getToken()] while 1: tok = self.getToken() if not tok.startswith('"'): break bits.append(tok) sval = "".join(bits)[1:-1] # and remove end quotes. sval = sval.replace('""', '"') # And fixup quotes in the body sd = StringDef(sid, self.addId(sid), sval) self.stringTable[sid] = sd def parse_bitmap(self, name): return self.parse_bitmap_or_icon(name, self.bitmaps) def parse_icon(self, name): return self.parse_bitmap_or_icon(name, self.icons) def parse_bitmap_or_icon(self, name, dic): self.getToken() if self.token=="DISCARDABLE": self.getToken() if self.token=="MOVEABLE": self.getToken() # PURE self.getToken() # bmpname bmf = self.token[1:-1] # quotes dic[name] = bmf def parse_dialog(self, name): dlg = DialogDef(name,self.addId(name)) assert len(dlg.controls)==0 self._dialogs[name] = dlg extras = [] self.getToken() while not self.token.isdigit(): self.debug("extra", self.token) extras.append(self.token) self.getToken() dlg.x = int(self.token) self.getCommaToken() self.getToken() # number dlg.y = int(self.token) self.getCommaToken() self.getToken() # number dlg.w = int(self.token) self.getCommaToken() self.getToken() # number dlg.h = int(self.token) self.getToken() while not (self.token==None or self.token=="" or self.token=="END"): if self.token=="STYLE": self.dialogStyle(dlg) elif self.token=="EXSTYLE": self.dialogExStyle(dlg) elif self.token=="CAPTION": self.dialogCaption(dlg) elif self.token=="FONT": self.dialogFont(dlg) elif self.token=="BEGIN": self.controls(dlg) else: break self.dialogs[name] = dlg.createDialogTemplate() def dialogStyle(self, dlg): dlg.style, dlg.styles = self.styles( [], win32con.WS_VISIBLE | win32con.DS_SETFONT) def dialogExStyle(self, dlg): self.getToken() dlg.styleEx, dlg.stylesEx = self.styles( [], 0) def styles(self, defaults, defaultStyle): list = defaults style = defaultStyle if "STYLE"==self.token: self.getToken() i = 0 Not = False while ((i%2==1 and ("|"==self.token or "NOT"==self.token)) or (i%2==0)) and not self.token==None: Not = False; if "NOT"==self.token: Not = True self.getToken() i += 1 if self.token!="|": if self.token in win32con.__dict__: value = getattr(win32con,self.token) else: if self.token in commctrl.__dict__: value = getattr(commctrl,self.token) else: value = 0 if Not: list.append("NOT "+self.token) self.debug("styles add Not",self.token, value) style &= ~value else: list.append(self.token) self.debug("styles add", self.token, value) style |= value self.getToken() self.debug("style is ",style) return style, list def dialogCaption(self, dlg): if "CAPTION"==self.token: self.getToken() self.token = self.token[1:-1] self.debug("Caption is:",self.token) dlg.caption = self.token self.getToken() def dialogFont(self, dlg): if "FONT"==self.token: self.getToken() dlg.fontSize = int(self.token) self.getCommaToken() self.getToken() # Font name dlg.font = self.token[1:-1] # it's quoted self.getToken() while "BEGIN"!=self.token: self.getToken() def controls(self, dlg): if self.token=="BEGIN": self.getToken() while self.token!="END": control = ControlDef() control.controlType = self.token; #print self.token self.getToken() if self.token[0:1]=='"': control.label = self.token[1:-1] self.getCommaToken() self.getToken() elif self.token.isdigit(): control.label = self.token self.getCommaToken() self.getToken() # msvc seems to occasionally replace "IDC_STATIC" with -1 if self.token=='-': if self.getToken() != '1': raise RuntimeError, \ "Negative literal in rc script (other than -1) - don't know what to do" self.token = "IDC_STATIC" control.id = self.token control.idNum = self.addId(control.id) self.getCommaToken() if control.controlType == "CONTROL": self.getToken() control.subType = self.token[1:-1] # Styles self.getCommaToken() self.getToken() control.style, control.styles = self.styles([], defaultControlStyle) #self.getToken() #, # Rect control.x = int(self.getToken()) self.getCommaToken() control.y = int(self.getToken()) self.getCommaToken() control.w = int(self.getToken()) self.getCommaToken() self.getToken() control.h = int(self.token) self.getToken() if self.token==",": self.getToken() control.style, control.styles = self.styles([], defaultControlStyle) #print control.toString() dlg.controls.append(control) def ParseStreams(rc_file, h_file): rcp = RCParser() rcp.parseH(h_file) try: rcp.load(rc_file) except: lex = getattr(rcp, "lex", None) if lex: print "ERROR parsing dialogs at line", lex.lineno print "Next 10 tokens are:" for i in range(10): print lex.get_token(), print raise return rcp def Parse(rc_name, h_name = None): if h_name: h_file = open(h_name, "rU") else: # See if same basename as the .rc h_name = rc_name[:-2]+"h" try: h_file = open(h_name, "rU") except IOError: # See if MSVC default of 'resource.h' in the same dir. h_name = os.path.join(os.path.dirname(rc_name), "resource.h") try: h_file = open(h_name, "rU") except IOError: # .h files are optional anyway h_file = None rc_file = open(rc_name, "rU") try: return ParseStreams(rc_file, h_file) finally: if h_file is not None: h_file.close() rc_file.close() return rcp def GenerateFrozenResource(inputFilename, outputFilename): """Converts an .rc windows resource source file into a python source file with the same basic public interface as the rest of this module. Particularly useful for py2exe or other 'freeze' type solutions, where a frozen .py file can be used inplace of a real .rc file. """ rcp = rcparser.ParseDialogs(inputFilename) in_stat = os.stat(inputFilename) out = open(outputFilename, "wt") out.write("#%s\n" % outputFilename) out.write("#This is a generated file. Please edit %s instead.\n" % inputFilename) out.write("_rc_size_=%d\n_rc_mtime_=%d\n" % (in_stat[stat.ST_SIZE], in_stat[stat.ST_MTIME])) out.write("class FakeParser:\n") out.write("\tdialogs = "+repr(rcp.dialogs)+"\n") out.write("\tids = "+repr(rcp.ids)+"\n") out.write("\tnames = "+repr(rcp.names)+"\n") out.write("\tbitmaps = "+repr(rcp.bitmaps)+"\n") out.write("def ParseDialogs(s):\n") out.write("\treturn FakeParser()\n") out.close() if __name__=='__main__': if len(sys.argv) <= 1: print __doc__ print print "See test_win32rcparser.py, and the win32rcparser directory (both" print "in the test suite) for an example of this module's usage." else: import pprint filename = sys.argv[1] if "-v" in sys.argv: RCParser.debugEnabled = 1 print "Dumping all resources in '%s'" % filename resources = Parse(filename) for id, ddef in resources.dialogs.items(): print "Dialog %s (%d controls)" % (id, len(ddef)) pprint.pprint(ddef) print for id, sdef in resources.stringTable.items(): print "String %s=%r" % (id, sdef.value) print for id, sdef in resources.bitmaps.items(): print "Bitmap %s=%r" % (id, sdef) print for id, sdef in resources.icons.items(): print "Icon %s=%r" % (id, sdef) print |