| Home Page | Recent Changes | Preferences

# Templating In Python

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

- Open Source
- Python

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In