Python: local variable 'count' referenced before assignment

I am having some trouble assigning a value to a variable in python.

Let me explain: I have a momentary button, connected to a null, connected to a chopexecute DAT set to Off to On.
for simplicity, let’s say I want to count how many times I press the button.

if in my script I type:

count = 0
def onOffToOn(channel, sampleIndex, val, prev):
	count += 1
	print(count)
	return

I get a “UnboundLocalError: local variable ‘count’ referenced before assignment” error. I guess because the count is set to 0 outside the onOffToOn function. Fair enough.

If I type:

def onOffToOn(channel, sampleIndex, val, prev):
	count = 0
	count += 1
	print(count)
	return

obviously the counter resets to zero every time the script is executed, and I always get 1 as value for my counter

and finally, if I type

def onOffToOn(channel, sampleIndex, val, prev):
	#count = 0
	count += 1
	print(count)
	return

I get the same error as I had in my first example “UnboundLocalError: local variable ‘count’ referenced before assignment”.

can somebody point me in the right direction?

PS: I am aware of the counter CHOP, the example above is for simplicity. Thanks in advance!

I found the answer. If anyone else was wondering, the code to make this working seems to be:

count = 0
def onOffToOn(channel, sampleIndex, val, prev):
	global count
	count  += 1
	print(count)
	return

There are major debates on several python’s online forums about whether is a good thing to use global variables or not… but I guess that for my current TD project this global variable line of code would do for now unless someone has a different coding trick to share!

Another way to think about this is to ask yourself where that variable lives - does this belong to a single op, to a component, to all of TouchDesigner?

You might also need to ask yourself - will any other operator need to know the value of this variable?

For many TouchDesigner projects, the approach you describe is probably just fine - that said, there are some cases you might also consider.

The approach you’re using will mean that each execute will contain a local context for your count.

Example 1

Here each button has it’s own counter. Any changes to your execute code will mean your counter will start back at 0.

Example 2

Here both buttons share the same execute, so each will increment the same count variable.

Both of these can produce results that don’t feel directly intuitive IMO - and will mean that you will have a harder time getting to that count variable with any other operator.

Example 3

These days I usually encourage folks to use custom parameters or another tool (storage, extensions, tables, CHOPs) for holding variable increments.

The code for this is straightforward:

settings = op('base_settings')

def onOffToOn(panelValue):
	settings.par['Count'] += 1

	return

IMO this makes what you’re doing a little less ambiguous - additionally the value doesn’t reset when you change your execute code, parameter values are saved in your TOE or TOX file, and you have access to this value outside of the context of your script.

Here are those examples for reference:
base_execute_examples.tox (1.7 KB)

1 Like

Wow Matt @raganmd ! thank you so much for sharing these tips!! I like your thinking.

and thanks for attaching the .toe file, I’ve learned so much.
I often use a Button connected to a null Chop connected to a Chop execute, totally forgot about the panel execute! Learning #1.

Example 1: very good point, I didn’t consider that any change to the execute code will restart the counter from 0. In my case the counter was just an example but I still see your point, totally valid and something to keep in mind for sure.

Example 2: Agree with you; and beyond that somehow I never thought to control the same execute from two different panels. Sometimes could be handy.
Learning #2 for me.

Example 3: I use custom parameters a lot, but never realized how in fact they could function as a way to hold a variable value, accessible from anywhere in the network, and that’s learning #3

If I set a Global OP Shortcut (I personally like to keep base names very short and use the expression ‘me.name’ in the Global OP Shortcut expression field) and use op.base_settings.par[‘Count’] that data can be retrieved easily from anywhere, regardless to where the base component is in the network, or where I need to retrieve that data.
And with the custom parameter set to ‘read only’ there’s no way to mess with that parameter if it’s really sensitive.

Thank you so much Matt, very inspiring

1 Like