How to create a python global variable?

Hi, I’m looking to create a python global variable that can be accessed (and maybe updated) across different nodes.

I have managed to achieve something similar by adding user defined parameters, but Natron’s execution of this is a bit clunky. Sometimes a frame will get the value it should have had 3 or 4 frames back (e.g. 3,4,5,6,7,4,9,10… which is weird), and it also clutters up the node graph with unnecessary connectors.

Global variables are usually bad. especially when you want to process frames in parallel. Why do you need it?

I want to be able to synchronize the animation (Transform and other nodes) of different image objects, without having to duplicate and maintain large chunks of parallel code across several different nodes.

What do you mean by “bad”?

Frame are rendered in parallel in Natron, not sequentially, so that’s totally normal.
You should store all your variables in a node, either as node parameters or as user parameters. If you don’t want to setup links everywhere, you can also use expressions. But don’t use global variables for anything else than constants. To declare a variable as being global, just do it the python way: “global x”.

1 Like

So there’s no way to control the rendering sequence?

At least I can use global variables to set start and end frames for sequences then, although if I understand you correctly, even if I set my global variables in a node in frame 1 theoretically there’s no guarantee that they will be available at frame 50.

I am using expressions, it’s just that I would prefer to define my expression once and be able to leverage it across multiple nodes.

Can I define a python function and access it from different nodes?

It should be possible to make plugins that render only sequentially, but that’s not implemented in Natron.
I don’t understand why you need global variables:

  • define your expression on one node
  • either link the parameter of all other nodes to this one, or use an expression.

You can also define toolsets in Natron, which are macros that can be executed from a menu. These can create complex graphs programatically.

See https://natron.readthedocs.io/en/rb-2.3/devel/groups.html#toolsets and https://github.com/NatronGitHub/Natron/blob/RB-2.3/Gui/Resources/PyPlugs/SplitAndJoin.py

1 Like

What I’m looking to do is coordinate animation over different objects. Say I have a curved arrow outline. I want to have 2 “copies” of that arrow at 120° and 240°, and I want them to rotate. At a certain frames I want that rotation to start to speed up or slow down, and I want to change the color inside that outline. This is just a simple example.

So I can easily define the frame points as global variables because they will be constants, but I want to be able to coordinate/sync the rest of the animation logic. If I link the rotation parameter I lose the 120° separation. If I use user parameters then I have the same rendering sequence issue that I do with global variables.

I can certainly copy all of my expressions from node to node but it is very unwieldy, especially while I am developing and testing my animations.

Actually I discovered that if I set
Preferences>Threading>Number of Render Threads to -1
then all the frames render in sequence and my variables work.

But please tell me why global variables are considered to be bad (other than for constants).

you demonstrated yourself why it is bad … you need to do locking to write to the variable from multiple threads or you get unexpected side effects. which then blocks to run your code in parallel. which usually is a huge speedup.

that’S why in most cases everyone tries to avoid them.

1 Like

Thanks darix. I guess I have a slightly different aim from most Natron users, and thanks to the answers I received here I have a better understanding of how to achieve it.

I’m not saying global variables are bad per se, all I’m saying is that Natron parameters are made for that. You can store any value at any time (keyframe) and retrieve it asynchronously. This is precisely what user parameters are made for.

Just create a “master node” in your project (it can be a dummy node such as an instance of NoOp), add user parameters to this node (cog icon in the parameter panel titlebar), and then you can use these parameters in any expression in your project, and you can even animate these over time or set keyframes.

I played with the user parameters and didn’t like them too much. They are clunky to setup and refer to, they distribute the animation synchronization calculations over multiple nodes when my purpose is to centralize them, and they really clutter up the node graph with (from my perspective) unnecessary connectors.

Maybe what I should do is define global arrays (over the length of the timeframe) and do all the calculations “up front”.