Extensions

From TouchDesigner 088 Wiki

When creating a component with specific functionality, it is often desirable to also extend the component's scripting options. For example, one may want to create a component to play back movie files. In addition to input and output operators, Extensions allow for extending the component with specific functions such as 'Preload all Movies' or 'Go to Movie Number 5' etc. Extensions also allow for extending the component with specific data, Properties.

Any component in TouchDesigner can be extended in Python. This can be accomplished through the use of Extensions.

Extensions are specified as a list of Python objects on the Extensions page of a component. Each of the extension objects can then be accessed by other operators, either directly or through an automatic search on the object's type.

Examples can be found in Samples/Learn/PythonExamples.toe of the installation folder.

TIP: Read Mathew Ragan's explanation of Extensions on his blog: Mathew Ragan: Understanding Extensions

See also: Internal Shortcut which is also on the Extensions page.


Example

TIP: On the Customize... dialog found on the RMB menu on a component, is a tab called Extensions where you can automatically create the required extension nodes. What you find below is a walk-through of the same thing.

Create a new component called base1. In this component add a Text DAT called text1. In this DAT, define two classes by entering the following text:

class MyClass1:
    def triple(self, v):
        return v*3
 
 class MyClass2:
    def triple(self, v):
        return v*3

Normally these class definitions can be accessed as a module with expressions such as mod.text1.MyClass1 and mod.text1.MyClass2. See: Creating Internal Modules from DATs for more information.

In this case however, specific instances of those classes will be created in a component extension.

Create a Geometry Object Component in base1 named geo1.

In two of geo1's Extension Objects parameters, enter:
mod.text1.MyClass1() and mod.text1.MyClass2()

Click Re-Init Extensions to initialize these extensions in the component.

Bringing up Operator popup info on geo1 (middle clicking on the node) should now show MyClass1 and MyClass2 as extensions.

Create a Box SOP inside of geo1. We wish to access the above methods in the SOP's parameters. There are a few ways to do this.

Firstly, we can access the extension objects directly, if we know where they exactly are. In the box tx parameter enter:

op('..').extensions[0].triple(0.33)

This will look in parent geo1, use its first extension object to access the triple() method. More generally though, we will not know where an extension is located, but what type of extension we are looking for.

Instead in parameter tx now enter:

ext.MyClass1.triple(0.33)

This will search upwards through all component parents, until it finds an extension object of type MyClass1, callings its triple method.

Lastly, an extension may not live in a direct parent component, but in a sibling etc. In that case, the ext search may be started from any arbitrary location:

op('/project1/base1/geo1').ext.MyClass1.triple(0.33)

Like the first example, the above will search geo1 and all of its parents until it finds an extension of type MyClass1. However one can specify any starting operator location.

Naming Extesions

By default, ext.type searches for extension instances matching type, example: ext.MyClass, however extensions can be named with the Extension Name parameters. Naming an extension, will then allow searching by that specific name instead.

Promoting Extensions

If an extension is promoted in a component, its methods and members are immediately available at the OP level. Each extension in a component can be individually promoted with its corresponding Promote Extension parameter on the Extensions page.

Note that all promoted methods and members must begin with a single capital letter, and be followed by lowercase letters, or they will result in an error. If you rename the function triple() in the previous example to Triple(), then you can use the following examples below once the extension is promoted:

Example:

op('../base2').Triple(12.3)

instead of

op('../base2').ext.MyClass1.Triple(12.3)

Dependencies

Often, when you work with Extensions, you'll want to reference properties or members or storage and have any references to those values updated dynamically. This functionality can be created by using Dependencies.

Properties

Custom operator properties can be created using Extensions. Below is a simple example of creating a property named FuelLevel in an extension named Car:

class Car:
    def __init__(self):
        self.FuelLevel = 100

This basic example creates a class named Car and upon creation, it initializes with a property named FuelLevel. If you attach this extension to a Base COMP named base1, and wanted to query the value you could use the code below:

op('base1').FuelLevel

This would return the value 100. If you wanted to update this value, you could use the code below:

op('base1').FuelLevel = 99

In this implementation, these properties would not create dependencies for any operators referencing them. You can find an example of creating properties with dependencies on the Dependencies page.

Things to Note

Make sure to specify instances, and not class definitions. Example:

me.mod.text1.MyClass()

not:

me.mod.text1.MyClass  # wrong, specifies a class, not an instance of one.

Template

Note: you will have to convert pasted text to have tabs vs spaces.

"""
Extension classes enhance TouchDesigner components with python. An
extension is accessed via ext.ExtensionClassName from any operator
within the extended component. If the extension is promoted via its
Promote Extension parameter, all its attributes with capitalized names
can be accessed externally, e.g. op('yourComp').MyFunction().

# Extension functions:
  def internal(self, x):
     debug(self.ownerComp, x)

  def Promoted(self, x):
     debug(self.ownerComp, x)
 
# The following examples can be pasted directly into your extension functions:

     self.a = 0 # attribute
     self.A = 0 # promoted attribute
     self.ownerComp.par.x = 0 # set a parameter
     self.ownerComp.A # only works when extension is promoted

# Use the following in __init__ to set up properties or storage

     # createProperty makes a simple property for an attribute
     # dependable values will cause cooks on change when used in expressions
     TDF = op.TDModules.mod.TDFunctions
     TDF.createProperty(self, 'MyProperty', value=0, dependable=True, readOnly=False)

     # storage manager creates properties that will remain across toe saves
     # and extension re-initialization
     StorageManager = op.TDModules.mod.TDStoreTools.StorageManager
     storedItems = [
        {'name':'StoredProperty', 'default':0, 'readOnly': False, 'property':True, 'dependable': True}
     ]
     self.stored = StorageManager(self, ownerComp, storedItems)

"""