Execute DAT cannot see some local variables but not others

Hi could anyone please point me to why the ‘udpoutOP’ local variable is fine when used as such in an Execute DAT but the ‘frameCounter’ variable gives me an uninitialized error?

# me - this DAT
# 
# frame - the current frame
# state - True if the timeline is paused
# 
# Make sure the corresponding toggle is enabled in the Execute DAT.

updoutOP = op('udpout')
frameCounter = 0

def onStart():
	frameCounter = 0
	return

def onCreate():
	return

def onExit():
	return

def onFrameStart(frame):

	numFrames = me.parent().par.Pulseinterval
	frameCounter += 1
	
	if (frameCounter > numFrames and me.parent().par.Enable):
		updoutOP.send(me.parent().par.Messagestring)
		print("sending")
		frameCounter = 0

	return
...

gives the error:

UnboundLocalError: local variable 'frameCounter' referenced before assignment

I am an experienced interactive software developer coming from C++, C#, Js and Python. I’m trying to navigate the TD flavour of Python which seems very idiomatic and full of ‘quirks’. Any help would be appreciated. Thanks

Hi @felixfaire, welcome to the forum!

What you have encountered here is the difference between local and global variables in Python.
(by the way this is default behavior in Python, and it has got nothing to do with TouchDesigner).

I recommend to read this Python tutorial which explains the basic concept of global variables with some examples:

You can see in the examples on that page that any global variable can be read in a function (as you noticed by successfully using your global udpOutOP variable) . But in your onFrameStart function you are trying to change the value of a global variable. This is only possible in Python if you use the global keyword (see last example in tutorial page linked above) , otherwise Python thinks you are assigning a new variable. So you can solve your issue by changing the onFrameStart method by this code below:

def onFrameStart(frame):
    global frameCounter
	numFrames = me.parent().par.Pulseinterval
	frameCounter += 1
	
	if (frameCounter > numFrames and me.parent().par.Enable):
		updoutOP.send(me.parent().par.Messagestring)
		print("sending")
		frameCounter = 0

	return

(the only change is the new line global frameCounter on the first line of the function)

PS now you will probably understand already that although you did not get an error notification, you will have to insert a global frameCounter line as well to your onStart function for your code to work correctly…

Thanks for the swift response. i thought i was missing something, this makes sense. I’ve changed the code to use absTime.frames instead as i dont really want to pollute the global scope.

I’ll add that the ‘global scope’ in this case is only that script. You aren’t polluting scope into other scripts, so it’s a reasonable workflow to use global this way.

1 Like