The Interactive & Immersive HQ

Python & TouchDesigner Cheat Sheet: Part 2 (Intermediate)

One of the most powerful features of TouchDesigner for developing projects is the thorough integration of Python within the program. Almost anything you can think of within the program has an associated class, member, and/or method, allow for the development of basically whatever complex project functionality you can dream up.

A couple of years ago, we posted a TouchDesigner Python Cheat Sheet for Developers, which, while comprehensive, was by no means complete! In this post, we’ve put together another Python and TouchDesigner cheat sheet, this time aimed at the intermediate level developer who has moved beyond CHOP references into scripting.

td Module

See: td Module page in the TouchDesigner Documentation

You’re probably familiar with a lot of the td Module’s functionality without even realizing it! The td module contains all TouchDesigner related Python classes and utilities, and is automatically imported. It contains commonly used members like me, op, parent and absTime. There are a number of useful functions that can be helpful as you’re developing projects, including debugging tools, accessing monitor info, project info, and more.

Python ExpressionDescriptionExample
debug(*args)Print all args and extra debug info (default is DAT and line number) to texport.

TIP: Use debug instead of print when debugging Python scripts in order to see object types and the source of the output.
a = op('noise1')[0]
debug(a)
# Results in textport: type:Channel name:chan1 owner:/project1/noise1 value:0.511246
(Debug - DAT:/project1/text1 fn: line:2)
monitorsReference to the group of available monitors. The Monitors class has a number of useful members accessible for each monitor, including which monitor is primary, width, height, and left/right/top/bottom positions of the combined monitor area.len(monitors) # number of monitors
monitors[0] # first monitor in the list
monitors[0].width # width of first monitor in the list
opexAn operator finder object, for accessing operators through paths or shortcuts. Works like the op() shortcut method, except it will raise an exception if it fails to find the node instead of returning None as op() does. This is now the recommended way to get nodes in parameter expressions.opex('geo1') # returns geo1 operator
opex('geo*') # returns the first OP whose path matches the given pattern, relative to the inside of this operator.


# If no operator with the specified name/pattern is found, you'll receive an error that is more specific/clear than you would with op(). Here's an example:
Traceback (most recent call last):
File "", line "op('/project1/text1').run()"
td.tdError: File "/project1/text1", line 1
td.tdError: opex(): OP at path 'geo*' not found.
Results of run operation resulted in exception.
projectReference to the project session. The Project class includes a number of useful members, including name, folder, saveBuild, saveOSName, and more.project.name # returns the filename under which the project is saved
project.folder # returns the folder at which the project resides
project.saveBuild # returns the app build number when the project was last saved
project.saveOSName # returns the app operating system name when the project was last saved

tdu Module, Continued

See: tdu Module page in the TouchDesigner Documentation

In the last post, we looked at some of the TDU Module’s functionality, including random value generation, clamping values, and more. There’s still more to the TDU Module! This time around, we’ve included some useful functions that give you access to TouchDesigner’s very useful pattern matching and pattern expansion, along with other string-related processing tools.

Python ExpressionDescriptionExample
tdu.base(str)Returns the beginning portion of the string occurring before any digits. The search begins after the last slash if any are present.tdu.base('arm123') # returns 'arm'
tdu.base('arm123/leg456') # returns 'leg'
tdu.digits(str)Returns the numeric value of the last consecutive group of digits in the string, or None if not found. The search begins after the last slash if any are present. The digits do not necessarily need to be at the end of the string.tdu.digits('arm123') # returns 123
tdu.digits('arm123/leg456') # returns 456
tdu.digits('arm123/leg') # returns None, searching is only done after the last /
tdu.digits('arm123/456leg') # returns 456
tdu.expand(pattern)Return a list of the expanded items, following the rules of Pattern Expansion.tdu.expand('A[1-3] B[xyz]') # return ['A1', 'A2', 'A3', 'Bx', 'By', 'Bz']
tdu.match(pattern, inputList, caseSensitive=True)Return a subset of inputList, in which each element matches the pattern. Wildcards are supported.tdu.match('foo*', ['foo', 'bar']) # return ['foo']
tdu.match('ba?', ['foo', 'bar']) # return ['bar']
tdu.split(string, eval=False)Return a list from a space separated string, allowing quote delimiters.

string – Any Python object, as it will be evaluated as str(string). Parameters will work.
eval (Keyword, Optional) – If True convert any valid Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and None.
split('1 2.3 None fred "one \'2\'" "[1,2]"') #yields ['1', '2.3', 'None', 'fred', "one '2'", '[1, 2]']
split('1 2.3 None fred "one \'2\'" "[1,2]"', True) #yields [1, 2.3, None, 'fred', "one '2'", [1, 2]]

Get Our 7 Core TouchDesigner Templates, FREE

We’re making our 7 core project file templates available – for free.

These templates shed light into the most useful and sometimes obtuse features of TouchDesigner.

They’re designed to be immediately applicable for the complete TouchDesigner beginner, while also providing inspiration for the advanced user.

Python Storage

See: Storage page and Storage section of OP Class page in TouchDesigner Docs.

As you progress with Python in TouchDesigner, you’re likely to run into situations where you need to store some form of data. Whether that’s presets, locations of assets, or the current state of portions of your project, storage is the preferred way to work with persistent global data in Python, since it can store any data type.

Python ExpressionDescriptionExample
store(key, value)Setting a value in storage of a component nn = op('pattern1')
n.store('level', 0.5)
fetch(key, default, search=True, storeDefault=False)Getting a value from storage
key – The name of the entry to retrieve.
default – (Optional) If provided and no item is found then the passed value/object is returned instead.
storeDefault – (Keyword, Optional) If True, and the key is not found, the default is stored as well.
search – (Keyword, Optional) If True, the parent of each OP is searched recursively until a match is found
n.fetch('level') # returns 0.5
storageDirectly access the storage dictionaryn.storage # returns {'level': 0.5}
storage['keyname']Directly access a key in the storage dictionaryn.storage['level'] # returns 0.5
'keyname' in storageTest if a key exists in the storage dictionary'level' in n.storage # returns True
unstore(keys1, keys2..)For key, remove it from the OP’s storage dictionary. Pattern Matching is supported as well.n.unstore('level') # removes entry from this OP's storage for the key 'level'
n.unstore('*') # removes entries for all keys from this OP's storage

DATs, Continued

See: DAT Class in TouchDesigner Docs

We looked at working with Python and DATs last time, but this was mainly focused on table-based operations. As DATs are going to be the place where you write most of your Python scripts, we’ve chosen a text-based focus for this post, showcasing members/methods used for editing text, parsing content as JSON, saving text to file, and more. We’ve also included a few additional methods for table manipulation that are sure to come in handy!

Python ExpressionDescriptionExample
op(‘datName’).textGet or set contents. Tables are treated as tab delimited columns, newline delimited rows.op('text1').text = 'Hello world' # sets text1 DAT's contents to 'Hello world'
print(op('text1').text) # returns Hello world
op(‘datName’).jsonObjectParses the DAT as JSON and returns a Python objectprint(op('text3').jsonObject) # returns {'store': {'book': [{'category': 'reference', 'author': 'Nigel Rees', 'title': 'Sayings of the Century', 'price': 8.95}, {'category': 'fiction', 'author': 'Herman Melville', 'title': 'Moby Dick', 'isbn': '0-553-21311-3', 'price': 9.99}, {'category': 'fiction', 'author': 'J.R.R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}]}}
op('datName').run(arg1, arg2..., endFrame=False, fromOP=None, asParameter=False, group=None, delayFrames=0, delayMilliSeconds=0, delayRef=me)Run the contents of the DAT as a script, returning a Run object which can be used to optionally modify its execution.
See DAT Class Methods page for full description of each argument.
op('text1').run() # runs contents of text1 DAT as a Python script
op('datName').save(filepath, append=False, createFolders=False)Saves the content of the DAT to the file system. Returns the file path that it was saved to.
filepath – (Optional) The path and filename to save the file to. If this is not given then a default named file will be saved to project.folder
append – (Keyword, Optional) If set to True and the format is txt, then the contents are appended to the existing file.
createFolders – (Keyword, Optional) If True, it creates the not existent directories provided by the filepath.
op('text1').save() # saves in native format with default name
op('text1').save('output.txt') # saves as human readable format with filename output.txt in project folder
op('datName').deleteRow(nameOrIndex)Delete a single row at specified name or indexop('table1').deleteRow(4) # deletes row at index 4
op('datName').deleteCol(nameOrIndex)Delete a single column at specified name or indexop('table1').deleteCol('tx') # deletes column with name tx
op('datName').row(*nameOrIndexes, caseSensitive=True, val=False)Returns a list of cells from the first row matching the name/index, or None if nothing is found.
– nameOrIndexes – Include any number of these. If a string it specifies a row name, if it’s numeric it specifies a row index. Pattern Matching is supported.
– caseSensitive – (Optional) Specifies whether or not case sensitivity is used.
– val – (Optional) If set to true, returns list of cell item strings instead of list of Cell Class items.
op('table1').row(0) # returns [type:Cell cell:(0, 0) owner:/project1/table1 value:tx, type:Cell cell:(0, 1) owner:/project1/table1 value:ty, type:Cell cell:(0, 2) owner:/project1/table1 value:tz]
op('datName').col(*nameOrIndexes, caseSensitive=True, val=False)Returns a list of cells from the first column matching the name/index, or None if nothing is found. See above .row() listing for a description of argumentsop('table1').col(1) # returns [type:Cell cell:(0, 1) owner:/project1/table1 value:ty, type:Cell cell:(1, 1) owner:/project1/table1 value:-2, type:Cell cell:(2, 1) owner:/project1/table1 value:-0.5]

As in the previous article, the above information is adapted from the Derivative wiki for TouchDesigner, which is an indispensable tool for wrapping your head around the Python members and methods that the software offers. If you’re interested in diving deeper into Python in TouchDesigner, it’s well worth checking out the following wiki articles:

Wrap-Up

Once again, I hope that this post has given you a look at how neatly integrated Python is within the TouchDesigner environment, and provides you with a useful resource for some of the common Python members and methods that you’ll use as you develop projects in TouchDesigner. Happy programming!