Hi all, been struggling with this all day, so any help appreciated.
I have a component with some custom parameters and an extension. Two of these parameters are Pan and Distance, which I’ve also made properties using the TDF.createProperty() call.
I want two other properties called X and Y, and I want these to update their values automatically, so that as I change my Pan and Distance parameters, the object converts them to cartesian coordinates and assigns those coordinates to X and Y.
Nothing I’ve tried so far has worked, whether I make them properties or not, whether I reference the Pan/Distance parameters directly, or as class properties, the class evaluates them once when the extension is initialized and then the value is static. I’ve tried using the tdu.Dependency call a bunch of different ways; I’ve tried various approaches to GetX(), SetX(), etc., and I just can’t get the behavior I want.
I’ve accomplished this in a class I wrote outside of Touch that can be run in the command prompt, and it works fine, so I’m really hoping someone has some insight into how to make this work properly in Touch.
Okay, this was super helpful! I think I have a much better idea of what that function under the property decorator is actually doing. I do have a couple questions about exactly what’s going on, though.
In the tox I uploaded, the only way to get the tx/ty parameters to update based on pan or distance, is to reference the pan/distance properties into the Constant CHOP “prop_cooker”. This is very confusing to me, but is this because the dependencies declared in the class require a reference to force it to cook?
I would also like this to work in reverse, so that changing Tx/Ty will update Pan/Distance, and there doesn’t seem to be a way to do this without causing a feedback loop. Is there some lovely Pythonic way to do this? Since I’m going to force the user to choose between a coordinate system with the Cartesian toggle parameter, I can stick with the if-statements in the property functions, but this looks really ugly and inefficient, especially considering that according to spec, the user should be able to have 96 simultaneous instances of this component.
I see a couple things happening here - a big piece of this puzzle is actually the network com to both send and receive OSC messages to update you pars. I don’ think you actually want to bind to the OSC channels, since that makes for a messy question of ownership.
Instead, I think you want the par (or the extension) to be the source of truth that you push out to clients, and then use incoming messages to update the par (or the extension). I think part of you missing ingredient here is a setter to go along with your property. The a setter for your property should let you do the operations you’re after without needing case statements - since you’ll set the values for your properties based on which par is updated / changed.
In plain python that looks like
self._Myval = 0
def Myval(self, val):
self._Myval = val
my_object = bar()
my_object.Myval = 12
In TouchDesigner we might use that idea like this in an extension:
def __init__(self, myOp):
self.MyOp = myOp
self._MyPar1 = tdu.Dependency(0)
self._MyPar2 = tdu.Dependency(0)
'''setter syntax looks like
def nameOfTheMethod(self, val):
self._yourPrivateMember = val
in the case of a tdu.Depdency you update the .val property of the depdency.
def MyPar1(self, val):
self._MyPar1.val = val
self._MyPar2.val = val + .25
def MyPar2(self, val):
self._MyPar2.val = val
self._MyPar1.val = val - .25
You can then use an execute to update the dependent member:
It’s cook related, but not the way you’re thinking exactly. Your Pan property is what triggers the updated evaluation of T1 and T2 - if you’re not using this property, rather, if an operator doesn’t need this property then TouchDesigner doesn’t evaluate it. Like how many ops don’t cook if you’re not looking at them, if you’re not using Pan to drive a part of your network, then Touch’s optimization doesn’t saves that block from execution.
I think using a setattr approach here should help solve this problem. There’s a lot more python to make this work, but I think that gets you what you need. I think there are also some other more TouchDesigner / node based techniques you could use as well if you want to lighten the python load on this one.
I was planning on changing the OSC I/O to be handled by the extension; one goal is to port all of this to a standalone Python library. I had also tried using setters, but it seems like I had a lot of the right pieces, I just wasn’t putting them together properly. It’s finally working, though, following your examples and recommendations.
When I sat down to program in this functionality, I thought it was going to take an hour, and instead I spent about 11 hours coding in circles , so I really cannot thank you enough for this!
Hi @theArduinoGuy, myOp is passed into the class via the expression in the parent COMP’s extension object parameter like so: op("./Foo").module.Foo(me), where me refers to the component itself. This expression isn’t just a reference to the class you’re building, it actually instantiates an object. In pure Python, it looks something like this:
def __init__(self, bar):
self.bar = bar
myFoo = Foo(myBar)
This is not strictly necessary, it’s just a convenient way to get the parent COMP into the class so you can make calls to it.