Trigger touch-specific functions from python import

Hello all.

I’m trying to call a python function that is Touchdesigner specific (e.g.) op('operator').method.value from imported code. That, of course, doesn’t work out of the box and I would like to know what you think is the best way to implement this. I have a folder structure that is:

project/
   videos/
   audios/
   python/
      lib_1/
         import_1.py
      python1.py
      python2.py
      python3.py
   toe_file.toe

So each python_1 until python_n is in a text DAT that I will trigger every now and then. And these can access all the Touchdesigner functionalities. Now I have code running in separate classes that I developed to integrate with the timeline and make new threads to call stuff. It all works well but I would need to call a function in the lib (import_1.py) that is specific to Touchdesigner (e.g. change a TOP property) and that cannot happen by default since python doesn’t know what op is.

Any idea what might be the best way to solve this? I also thought about parent/child inheritance and shared variables but I might be going to the wrong direction…

Thanks.

the td-module gets imported per default in the global namespace, so the moment you import your python1.py into TD it should be able to work with all the default TD-Methods.
If that is not the case, you can also call

import td

In your external module to get access to the methods (then it would be td.op(“ioerator”).methid.value )

Another good way to handle something like this is by using an event-emitter-system.

class eventEmitter():
	def __init__(self):
		self.events = {}
		
	def On(self, event, callback):
		subscriber = self.events.get(event, [] )
		subscriber.append( callback )
		self.events[event] = subscriber
		
	def Emit(self, event, *args, **kwargs):
		for callback in self.events.get(event, []):
			callback( *args, **kwargs )

Thank you very much for the answer. Now I understand that this works since I got no errors on the import nor the module. But there is a problem here:

This is a multithreaded agent I built to trigger stuff. So in the case of Touchdesigner I have it running on a separate thread and printing a hello world to the terminal every second. I am trying to use this to change videos but it seems it’s not that easy? I did this calling the function td.op('level1').par.invert = 0.31 from the external lib.

Will try your event emitter, just need to lunch lol

Oh, I was not aware of the Threading-Issue. Not sure if the eventEmitter will help here.
What I do when working with threads is to have a loop running using an executeDAT checking values from the threaded-object and executing if that value should be triggered.
Actually, now I’m not sure if it might actually be feasable to use closures in this situation.

Edit:
In fact, closures work just fine. How curios.
Just a simple example, put this in text2


import threading
import time

class Increaser(threading.Thread):
   def __init__(self ):
        threading.Thread.__init__(self)
        self.executes = []

   def run(self):
        index = 0
        while(True):
            index += 1
            time.sleep(1)
            def closure():
                op("constant4").par.value0 = index
            self.executes.append( closure )

ThreadObject = Increaser()
ThreadObject.start()

and then use an executeDAT to iterate of the executes of the ThreadObject after importing text2.
(I crashed it after removing the time.sleep though.)
grafik

1 Like

Thank you very much for the response.

Now basically the trick is to let the Execute DAT check every frame if something is to be done right? I can import the separate library on the top and then get the onFrameEnd(frame) function checking (e.g. a variable) and doing TD specific functions right? Isn’t this expensive computationally?

Exactly. You are basicly creating your own async/await loop. So having a sink your thread pours data in and clearing it after reading is a good way to go. (I forgot to clear the execute-array in my codeexample.)
Checking every frame is not as bad as it sounds, esp if your are just checking every frame. Python is pretty fast in this regard. You would need 100000 checks to even notice much of a difference. Only overhead might be the executeDAT itself. So it might be a good idea to do the checks all in one executeDAT.

Thank you @alphamoonbase.

Just for clarity and documentation, can you share a simple example of a toe file? A really simple example with your execute() logic. It can just be a new separate thread that every second executes a text DAT with a print(“hello”).