I'm a doctor, not a mechanic

Legacy:Templating In Python

From Unreal Wiki, The Unreal Engine Documentation Site

Jump to: navigation, search

This was a quick, quick hack, because I got tired of having to do math when I move things in my mutator config or add elements. Just save the program text at the end to template.py, or whatever else you'd rather call it. It's a simple preprocessor which will generate new files from template files given on the command line. Any file specified will have its extension stripped to produce the name for the new file. Directories specified will be searched recursively for .template files, which will be processed into new files without the .template extension. Basically, it makes a clean environment for each file, in which commands embedded in the file can be executed. Anything between two lines starting with "!!exec" will be executed, but will produce no output in the processed file. Anything between a pair of "!!" on a line will be evaluated as an expression in Python, and will have its value converted to a string and inserted in place of the expression. The exec block feature is mostly for setting up constants. Also, you can change the formatting used for expression which yield floating-point values by setting "floatformat" to a Python format string in an exec block. Here's a quick sample, from some of the code I'm actually using this with (cut down a little bit):

This is part of RadarConfig.uc.template:

!!exec set up constants for GUI layout
gridw=21.0
gridh=19.0
llbll=1.0
nmlblw=4.0
elh=2.0
row1t=1.0
row2t=4.0
floatformat="%0.8f"
!!exec
defaultproperties
{
        Begin Object Class=GUIButton name=DialogBackground
                WinWidth=1.0
                WinHeight=1.0
                WinTop=0
                WinLeft=0
                bAcceptsInput=false
                bNeverFocus=true
                StyleName="ComboListBox"
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(0)=GUIButton'TacticalDisplay.RadarConfig.DialogBackground'
        Begin Object Class=GUILabel name=DialogText
                Caption="Tactical Display Configuration"
                TextAlign=TXTA_Center
                WinWidth=1.0
                WinHeight=!!elh/gridh!!
                WinLeft=0.0
                WinTop=!!row1t/gridh!!
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(1)=GUILabel'TacticalDisplay.RadarConfig.DialogText'
        Begin Object Class=GUILabel name=DetectRangeText
                Caption="Maximum Range"
                WinWidth=!!nmlblw/gridw!!
                WinHeight=!!elh/gridh!!
                WinLeft=!!llbll/gridw!!
                WinTop=!!row2t/gridh!!
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(2)=GUILabel'TacticalDisplay.RadarConfig.DetectRangeText'

Running template.py on it generates this:

defaultproperties
{
        Begin Object Class=GUIButton name=DialogBackground
                WinWidth=1.0
                WinHeight=1.0
                WinTop=0
                WinLeft=0
                bAcceptsInput=false
                bNeverFocus=true
                StyleName="ComboListBox"
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(0)=GUIButton'TacticalDisplay.RadarConfig.DialogBackground'
        Begin Object Class=GUILabel name=DialogText
                Caption="Tactical Display Configuration"
                TextAlign=TXTA_Center
                WinWidth=1.0
                WinHeight=0.10526316
                WinLeft=0.0
                WinTop=0.05263158
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(1)=GUILabel'TacticalDisplay.RadarConfig.DialogText'
        Begin Object Class=GUILabel name=DetectRangeText
                Caption="Maximum Range"
                WinWidth=0.19047619
                WinHeight=0.10526316
                WinLeft=0.04761905
                WinTop=0.21052632
                bBoundToParent=True
                bScaleToParent=True
        End Object
        Controls(2)=GUILabel'TacticalDisplay.RadarConfig.DetectRangeText'

I've deleted the rest of the controls (there are 26 in this dialog), and the constants that are associated with them, but you get the idea. to add a new control at the bottom of the dialog, for example, I can make some new constants to define its location, and change gridh, then rerun template.py.

And finally, here's the Python script:

#!/usr/bin/python
 
import os, sys, re, types
 
evalre = re.compile('!!(.*?)!!')
 
def visitor (arg, dirname, names):
        for filename in names:
                if filename[-9:] == ".template":
                        processfile(os.path.join(dirname,filename))
 
def processfile(filename):
        envlocals={}
        envglobals={}
        execstring=""
        inexec=0
        infile=file(filename,"r")
        outfile=file(os.path.splitext(filename)[0],"w")
        for line in infile.xreadlines():
                if line[:6]=="!!exec":
                        if inexec:
                                exec(execstring,envglobals,envlocals)
                                execstring=""
                                inexec=0
                        else:
                                inexec=1
 
                        continue
                if inexec:
                        execstring += line
                        continue
                index = 0
                newline = ""
                match = evalre.search(line,index)
                while match:
                        val = eval(match.group(1),envglobals,envlocals)
                        if type(val) == types.FloatType and envlocals.has_key("floatformat"):
                                val = envlocals["floatformat"] % val
                        else:
                                val = str(val)
                        newline += line[index:match.start()]
                        newline += val
                        index = match.end()
                        match = evalre.search(line,index)
                newline += line[index:]
                outfile.write(newline)
                infile.close
                outfile.close
 
for filename in sys.argv[1:]:
        if os.path.exists(filename):
                if os.path.isdir(filename):
                        os.path.walk(filename,visitor,None)
                elif os.path.isfile(filename):
                        processfile(filename)

In case your browse messes up the whitespace, you can also get the script in a file.

Related Topics

Personal tools