We’ve all used global variables in our projects for years. This could take form of asset paths, computer names, parameters, or really anything! Maybe you’ve got an XML file of settings, a CSV, or even an internal Table DAT. Whatever it is you’ve likely experienced that moment when you update the variable and found that different elements of your network don’t actually pull down the new data unless you force cook them. Today we’ll find out why and a great trick to get around it!
What is dependability?
In TouchDesigner there is something called dependability. This has nothing to do with stability or bugs or crashes. This is all about the relationship between an object in TouchDesigner and a piece of data in TouchDesigner. When a piece of data in TouchDesigner changes, the behind the scenes mechanisms in the engine understand that the new data needs to be pushed to wherever it’s needed and the operator on the other end needs to cook again with the new data. When it comes to Python, this dependability isn’t exactly built in, which is why you might experience some of the things I mentioned in the first paragraph. Sometimes this works thanks to TouchDesigner trying to cover us, but I prefer to always be explicit when possible and create systems that are 100% reliable. Thankfully there’s the Dependency Class in TouchDesigner’s Python that lets us build this 100% reliable data connections.
The dependency class is a great tool that allows us to create these reliable data objects inside of our extensions. Let’s get started by making a simple extension that initializes a new attribute. The easiest way to do this is to right click on a component like a Container COMP or Base COMP, click Customize Component, then add a new extensions in the middle of the window like below:
Then inside of our component we’ll see our extension script with a bunch of default code in it:
We’ll start by deleting almost everything inside of it so we’re just left with this:
from TDStoreTools import StorageManager import TDFunctions as TDF class Data: """ Data description """ def __init__(self, ownerComp): # The component to which this extension is attached self.ownerComp = ownerComp
Creating your first dependency
The easiest way to add a dependency is to create a tdu.Dependency object for your value. This can be done as simply as this:
from TDStoreTools import StorageManager import TDFunctions as TDF class Data: """ Data description """ def __init__(self, ownerComp): # The component to which this extension is attached self.ownerComp = ownerComp self.My_val = tdu.Dependency(20)
It’s just that simple! Now whenever you reference My_val from another operator or expression, any time that value gets updated it will force cook the operator and ensure the value is always kept up to date. This value can be an integer, a string, float, list, or dictionary. Now we’re not done just yet, there’s a few things we have to keep in mind.
Updating the value
An important thing to know is that if you need to update the value with Python, you must explicitly update only the value and not the whole object. What does this mean? This means we should never type something like:
op('container1').My_val = 50
While this might work fine for our non-dependable objects, this actually overwrites the dependency object with just a normal integer. You can think of the dependency object as a holder for your value. So instead we should specifically only change the value like:
op('container1').My_val.val = 50
What this does is keep the tdu.Dependency() object in tact while updating the value. This is super important because it can be tough to visualize your dependency object and easy to accidentally overwrite it with a non-dependable object.
So we’ve looked at creating a simple dependency and updating it’s value, but what about the use cases? Well we mentioned global variables like assets paths. I find putting asset paths into a dependency object GREATLY increases it’s reliability and usefulness on projects. I then combine this with something like a File In DAT that reads a csv file from the drive and updates the dependency. The cherry on top is using a global OP shortcut to be able to quickly access that extension. All of this together only takes a few more lines of code and looks like this:
In this example, the Evaluate DAT is only there to give you a quick preview of what the Assets attribute value is. But it’s really that simple! From anywhere in the project I could use a shortcut like op.VARS.Assets and I’ll have that dependable object pulled reliably. A system like that will ensure that any operator in your network pulling this data will always be kept up to date.
Whether you’re new to Python or a pro, dependency objects may not be something you’ve taken advantage of yet. They’re extremely handy for things like asset variables or variables coming in from an external file but they can functionally be used for any kind of data you want to reliably be pulled around a network. Hopefully this gets you up and running quickly and easy!