I love the smell of UnrealEd crashing in the morning. – tarquin

Difference between revisions of "Legacy:Templating In Python"

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search
(argh @ "here" links)
 
(One intermediate revision by one other user not shown)
Line 104: Line 104:
 
def visitor (arg, dirname, names):
 
def visitor (arg, dirname, names):
 
for filename in names:
 
for filename in names:
if filename[-9:] == ".template":
+
if filename.endswith(".template"):
 
processfile(os.path.join(dirname,filename))
 
processfile(os.path.join(dirname,filename))
  
Line 111: Line 111:
 
envglobals={}
 
envglobals={}
 
execstring=""
 
execstring=""
inexec=0
+
inexec=False
 
infile=file(filename,"r")
 
infile=file(filename,"r")
 
outfile=file(os.path.splitext(filename)[0],"w")
 
outfile=file(os.path.splitext(filename)[0],"w")
for line in infile.xreadlines():
+
for line in infile:
if line[:6]=="!!exec":
+
if line.startswith("!!exec"):
 
if inexec:
 
if inexec:
 
exec(execstring,envglobals,envlocals)
 
exec(execstring,envglobals,envlocals)
 
execstring=""
 
execstring=""
inexec=0
+
inexec=False
 
else:
 
else:
inexec=1
+
inexec=True
 
 
 
continue
 
continue
Line 132: Line 132:
 
while match:
 
while match:
 
val = eval(match.group(1),envglobals,envlocals)
 
val = eval(match.group(1),envglobals,envlocals)
if type(val) == types.FloatType and envlocals.has_key("floatformat"):
+
if type(val) == float and "floatformat" in envlocals:
 
val = envlocals["floatformat"] % val
 
val = envlocals["floatformat"] % val
 
else:
 
else:
Line 142: Line 142:
 
newline += line[index:]
 
newline += line[index:]
 
outfile.write(newline)
 
outfile.write(newline)
infile.close
+
infile.close()
outfile.close
+
outfile.close()
  
 
for filename in sys.argv[1:]:
 
for filename in sys.argv[1:]:
Line 154: Line 154:
  
 
In case your browse messes up the whitespace, you can also get the [http://chshrcat.homelinux.net/template.py script in a file].
 
In case your browse messes up the whitespace, you can also get the [http://chshrcat.homelinux.net/template.py script in a file].
 +
 +
==Related Topics==
 +
* [[Legacy:Open Source|Open Source]]
 +
* [[Legacy:Python|Python]]

Latest revision as of 00:49, 3 December 2009

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.endswith(".template"):
			processfile(os.path.join(dirname,filename))
 
def processfile(filename):
	envlocals={}
	envglobals={}
	execstring=""
	inexec=False
	infile=file(filename,"r")
	outfile=file(os.path.splitext(filename)[0],"w")
	for line in infile:
		if line.startswith("!!exec"):
			if inexec:
				exec(execstring,envglobals,envlocals)
				execstring=""
				inexec=False
			else:
				inexec=True
 
			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) == float and "floatformat" in envlocals:
				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[edit]