Introduction to Python Tutorial
- 1 Inputting Python
- 2 Working with Operators
- 2.1 Basic access
- 2.2 Common Python Tasks
- 2.3 Python in Parameter Expressions
Python in the Textport
The simplest method to input Python scripts is through the textport. The textport, like all scripting in TouchDesigner, allows scripts to be specified in either Python or Tscript.
After opening the textport, make sure it is set to Python language. This is controlled by the small toggle button in the upper left side of the textport.
When it is set to Py, all input is interpreted as Python. When set to T it is interpreted as tscript.
In addition the textport prompt and text color have different values for each language, making it easier to identify which state the textport is in:
As a simple test type the following into the textport:
This will output all help related to the
op() method found in the td Module.
The td Module is the main module containing all TouchDesigner related classes and objects. It is imported by default when the application begins.
Another useful module is the tdu Module. This module contains some specific TouchDesigner utility functions useful during scripting.
Python in DAT Scripts
Begin by placing down a new Text DAT. Make sure the operator's parameter language mode is set to Python. This is done by clicking the Operator Language button, located in the upper right corner of the operator dialog.
This mode controls the language of both the DAT contents, and its parameter expressions. In cases where the two use different languages are used, modify the Language parameter on the common page accordingly.
Click the Viewer Active Flag to edit its contents.
Enter the following two line script:
for i in range(1,5): print(i)
Note the first line should not include any leading spaces in this example, while the second line is indented. It should either be indented with a single tab or 4 spaces.
A single script can not mix indentation methods.
To run the script, right-click on the node and select Run Script from the popup menu.
The output will be echoed to the textport. Open the textport to see the digits 1 through 4 printed.
Part of the great power of Python is its access to the countless number of modules that have been created. The default installation includes all standard modules.
For example, open the textport and type:
import math print(math.sin(4))
This will import the default math module from the installation folder, making its methods available.
This is done most easily through the following steps:
- Install a parallel copy of the same version of Python to the hard disk.
The current version of Python shipped with TouchDesigner is 3.7. It can also be found here.. Python generally installs to location:
- Ensure the module you wish to use, is compatible with Python 3.7. Also ensure it is compatible with the specific version of TouchDesigner you are running (32 or 64 bit).
- Install the module to the parallel python installation (eg
C:/Python37/python.exeand import the module manually to make sure there are no errors.
Once the module is successfully installed, it will automatically be visible by TouchDesigner by default. This option is found under the Edit->Preferences menu as "Add External Python to Search Path". Alternatively you can add the search path by modifying the Preference labelled "Python 32/64 bit Module Path". Multiple paths are separated by semicolons (;). Finally you can modify the search path directly by either modifying the system environment variable PYTHONPATH or be executing a script which appends the path to sys.path as in the example below.
import sys mypath = "C:/Python37/Lib/site-packages" if mypath not in sys.path: sys.path.append(mypath)
Overriding built in modules
TouchDesigner may come with specific modules pre-installed. If you require a different version, find the TouchDesigner folder where the original is included and remove it from the search path before importing.
import sys sys.path.remove('C:\\Program Files\\Derivative\\TouchDesigner\\bin\\lib\\site-packages')
Creating Internal Modules from DATs
Python allows scripts to be organized as modules. This is done through the standard
In TouchDesigner the module can reside on disk like regular Python or they can also reside inside other DATs.
Place down a text DAT and rename it to
Ensure its parameter language is set to Python, activate its viewer, and enter the following function:
def my_adder(x,y): return 10*x+y
In the same component, add another text DAT. Rename it to
test_import and ensure its parameter language is set to Python as well.
In this DAT enter the following:
import my_utils a = my_utils.my_adder(1,3) print(a)
After running this script, the value of 13 will be printed in the textport.
my_utils DAT has been treated as a module, through the import statement above.
Just as TouchDesigner allows for Component Variables, and Component Time, so too can one create Component Modules.
These are simply DATs placed in a specific child of the component, namely its
Module Search Order
When attempting to import a module, TouchDesigner first searches in the current component for a DAT with that module name.
If it is not found, it then searches its component modules.
If still not found, it then searches the component modules of each parent until it reaches the root component.
If still not found, it then searches the component modules of /sys component.
If still not found, it then searches the regular Python disk search.
In this manner, one can place component modules at a specific parent location and know they will be found by all children and sub-children of that component.
For example, any DATs placed in
/local/modules will be accessible to all scripts and expressions wherever they are located.
Note: When you attempt to import an external module, it will first look for DATs of that name in the same folder , so be sure to avoid name conflicts keeping the above search order in mind.
Module On Demand
The import statement, though useful contains two disadvantages:
- Module names must be single words, so relative DAT paths cannot be used.
- They are unsuitable for use in a parameter expression.
To avoid this, use the Module On Demand or mod object.
In the DAT
test_import, replace the contents with the following code:
a = mod.my_utils.my_adder(1,3) print(a)
Notice how no import statement is needed, making this evaluation suitable for parameters. The same search rules apply when using the module on demand object.
Finally, string paths, including relative paths may be used. Simply pass in the string as a parameter to the mod object:
a = mod('my_utils').my_adder(1,3) print(a)
Both absolute and relative paths may be used:
a = mod('/project1/my_utils').my_adder(1,3) print(a)
All versions will result in the same output.
Working with Operators
The main class type describing any Operator is the base OP Class. You will need a reference to one of these to do anything. There are two global operator objects are always available (except for in the Textport):
merefers to the operator that is currently being evaluated or executed. For example, when executing a script,
merefers to the containing DAT. When evaluating an expression,
merefers to the containing operator.
rootrefers to the top level component
To get references to other OPs (for example, a node named
'wave1' sitting next to the node
'constant1') the most common functions to use are:
ops(), for example
op() returns a single OP object, while
ops returns a (possibly empty) list of OPs.
They are described in td Module.
These functions search for operators from the current component, so both relative and absolute paths are supported.
The current component is defined as: The OP that
me is inside.
Note that the OP Class itself, also contains an
ops() method. In this case, nodes are searched from the OP.
me.op('..') will always return its own parent, while
op('..') will return the parent of the current component.
If you are typing a python expression in a parameter of a node
'constant1', and you wish to get a reference to
'wave1', you would type
If you are in a script you can assign this reference to a variable for easier repeated access.
n = op('wave1')
In this case
op() will search relative to the DAT that is executing the script.
An OP also has a
parent() method that can be use to get the parent COMP of it.
If you are putting a Python statement in a parameter of a COMP and want to refer to a child of that COMP, you can use the
op() method for the OP, which is available as
me in the parameters.
TIP: To find out quickly what members and methods you have access to for any node, select that node and on its parameter dialog, click the Python Help icon. You will go the wiki for the python classes for that node. There you can find out what info you can get about a node, and what methods are available for it. The documentation can also be arrived at by right clicking on the node and selecting "Python Help..." from the menu.
Place down a new Text DAT, ensure its parameter flag is set to Python, and enter the following script:
n = op('/project1') m = n.ops('text*') for a in m: print(a.name)
After running the script,
n is assigned the results of the global function, while
m is assigned results relative to
Some useful members and methods of an OP object, are:
These are described in OP Class.
Notice the last attribute,
parent() is a function. It takes an optional argument specifying how far up the parent chain to climb.
To see how they are used in practice, put down a new Text DAT, ensure its Parameter Language parameter is set to Python and enter the following code:
print('i am ', me) print('child of ', parent()) print('grandchild of ', parent(2)) print('root children:') k = root.children for r in k: print(r)
The resulting details will be found in the textport.
Common Python Tasks
|Creating an OP (Sphere SOP)|
|Creating a named OP|
|Copying OPs (Nodes)|
|Deleting an OP|
|Renaming an OP|
|Changing an OP's type|
|Changing multiple OPs' types|
|Setting an OP's comment|
|Changing an OP's parameter|
|Changing an OP's parameter
with more than 1 value
|Pulsing a parameter value|
|Cooking an OP|
|Saving an OP's data to a file|
|Changing an OP's Render and Display Flags on|
|Loading a .tox file into a COMP|
|Wiring operators together|
|Clicking gadgets (panel components)|
|Querying another OP's parameter|
|Querying a parameter in the same OP|
|Getting Info CHOP channels from an OP
without cooking it
|Getting an OP's parent|
|Getting an OP's grand-parent|
|Getting an OP's name|
|Getting an OP's parent's name|
|Getting digits of an OP's name in its parameters|
|Getting digits of an OP's parent's
name in its parameters
|Getting digits of another OP's name|
|Getting an OP's type|
|getting a unique random number each frame|
|getting a unique random number per numbered operator|
|Checking for an OP's existence|
|Getting the number of children of a COMP|
|Getting the number of inputs of a multi-input OP|
|Getting Info CHOP channels from an OP, width is a member|
|Conditional "if" in one line of a parameter|
|Conditional "if" alternative|
|Convert space separated string to a list|
|Conditional list comprehension|
|Test operator type|
|Test operator family|
"Absolute Time" is the time since you started your TouchDesigner process, not counting when your power button was off (top bar).
|Retrieving a node's local frame number|
|Retrieving a node's local time in seconds|
|Retrieving absolute time in frames|
|Retrieving absolute time in seconds|
Storage in Python
Storage is the preferred way to work with persistent global data in Python, since it can store anything data type.
|Setting a value in storage of a component
|Getting a value from storage|
|Directly access the storage dictionary|
|Directly access a key in the storage dictionary|
|Test if a key exists in the storage dictionary|
Variables are always text strings.
|Setting a value|
|Setting a Root Variable|
|Setting a Component Variable
at the current component
|Setting a Component Variable
at another component
|Setting a Path Variable||Set the Path Variable parameter of any parent component and use |
Print versus Debug
The above code makes uses of the built in
debug statement can be used to obtain more specific information about a python object, and will also append the line number and script in which
debug was called.
Simply replace all occurrences in the above example to obtain more detailed information:
debug('i am ', me) debug('child of ', parent()) debug('root children:') k = root.children for r in k: debug(r)
debug function can be used anywhere the
Alternatively, one could always print identifying attributes specific to the object:
Python in Parameter Expressions
Begin by placing down an Geometry Component. Make sure the Geometry Object is called
geo1 and its Language Parameter is set to Python (as described earlier).
Toggling this flag will automatically convert all existing parameter values to the specified language where it can.
In its Translate X parameter (first) field enter:
The resulting expression now evaluates to
Similarly, a parameter can refer to another parameter value from any location, including the same operator.
This is done through the Par Class. Its main access is
In the above field, change the expression to:
Now the Translate X value will contain whatever is in the Translate Y (second) field. Modify the Y value to confirm.
As Parameter objects are automatically converted to numbers or strings when needed, the above can be shortened to:
Quotes and String Constants
All python parameter fields are always treated as expressions, so when entering a string constant, it must be contained within single or double quotes, otherwise it will be interpreted as a specific python object, resulting in an error.
For example, in the
material parameter of geo1.
Setting it to:
phong1 will result in an error, as
phong1 cannot be found.
It must be therefore be set to:
to denote a literal string constant.
The interface to the string parameter fields will be changed in the future to avoid this requirement.
Scripting Parameter Values
In addition to manually entering parameter values and expressions, they may also be scripted. Place down a new Text DAT. Ensure its parameter language is set to Python.
In the Text DAT enter the following:
n = op('geo1') v = n.par.rx.eval() print(v) n.par.rx.val = 30
When run, the current value of
rx parameter of
geo1 will be printed, then changed to
In this case, the
eval() call explicitly returns the parameter value, but it can automatically be evaluated as needed in many cases:
#examples of automatic casting v = float(n.par.rx) v = n.par.rx + 5 v = n.par.rx * n.par.ry
Similarly, the same method can be used to directly set the value of a parameter without using the
#equivalent n.par.rx.val = 5 n.par.rx = 5
This class may also be used to set the parameters expression. In the above example change the last line to:
n.par.rx.expr = 'me.time.frame'
Run this code, and notice the
rx parameter is now set to the current frame, by referring to the operator's Time Component
Operators can be in Tscript mode or python mode by setting their Operator Language flag on the parameter. Some old files opening in 099 may have their nodes in tscript mode to keep things compatible, but any new nodes created in 099 are in python mode by default. Default Node Language can be changed in preferences.
A dialog box in which commands and scripts can entered manually. Script errors and python
print() messages are also output to the textport.
Any of the procedural data operators. OPs do all the work in TouchDesigner. They "cook" and output data to other OPs, which ultimately result in new images, data and audio being generated. See Node.
The panel at the bottom of TouchDesigner, it controls the current Time of the full system or just one component.
An Operator Family that creates, composites and modifies images, and reads/writes images and movies to/from files and the network. TOPs run on the graphics card's GPU.
Storage is a python dictionary associated with any operator, used to keep user-specified data within the operator.