Property Update on Animation Change

I created a relatively simple addon with two FloatProperties “x” and “y”. I’ve added an update function to the “x” FloatProperty which sets “y” equal to 2*x. It also prints this out when x is changed. Here’s the code:

"""
This program indicates that the update function is not called
when a property is changed via the animation system.
"""

bl_info = {
    "version": "0.1",
    "name": "No Update on Animate",
    'author': 'Bob',
    "location": "Properties > Scene",
    "category": "Blender Experiments"
    }


import bpy
from bpy.props import *


def update_y ( self, context ):
    """ The "self" passed in is an AppGroup object. """
    self.y = 2 * self.x
    print ( "y(%g) = %g" % (self.x,self.y) )


class AppGroup(bpy.types.PropertyGroup):
    x = FloatProperty(name="x", default=1.0, update=update_y)
    y = FloatProperty(name="y", default=2.0)


class APP_PT_Props(bpy.types.Panel):
    bl_label = "App Properties"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "scene"
    def draw(self, context):
        app = context.scene.app
        row = self.layout.row()
        row.prop(app, "x", text="x")
        row = self.layout.row()
        row.prop(app, "y", text="y = 2*x")

def register():
    print ("Registering ", __name__)
    bpy.utils.register_module(__name__)
    bpy.types.Scene.app = bpy.props.PointerProperty(type=AppGroup)

def unregister():
    print ("Unregistering ", __name__)
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.app

if __name__ == "__main__":
    register()


It works just fine, and changing “x” changes “y”. The update function prints these changes to the console. Now I want to animate “x” … and I expect that changing “x” will change “y”.

I placed two keyframes on “x” and I used a simple curve to change its value from 0.0 to 10.0 as time goes from 0 to 250. When I change the current frame via the time line, the value of “x” changes appropriately. But “y” remains unchanged. Nothing is printed to the console.


The “update” documentation for FloatProperty states:

update (function) – Function to be called when this value is modified, This function must take 2 values (self, context) and return None. Warning there are no safety checks to avoid infinite recursion.

It appears that the value of “x” is changing. The change shows up both in the drawn property and from the Python console. But it’s being changed through the animation system rather than the GUI. Should this matter?

Note that if I change “x” via the Python console:

C.scene.app.x = 4

Then the value of “y” will change instantly. Furthermore, moving current frame in the time line DOES change “x” … as can also be verified via the Python console (check the value of C.scene.app.x after moving the current frame in the time line).

Is this a bug? If it’s not a bug, is there any place that documents this?

Thanks in advance.

Sorry to bump this topic, but I’d like to understand if this is expected behavior or not. Thanks in advance.

Can’t tell you if it’s expected behaviour - never used that update parameter. The doc you reference is a little vague, but “modified” does have a “something the user does directly” quality about it, unlike if it said “… called whenever the value changes”. But there may be another way to do what you need, depending on what it is exactly that you’re after - your example is a little … abstract :slight_smile:

If you want to programmatically change something based on the animation of another thing, it may be worth looking at either drivers or (to keep it in the code) look at adding a frame_change handler to trigger your update.

What is it you’re trying to do?

Update: It turns out that in addition to the “update” function, there’s also a “set” function and a “get” function. These appear to be called whenever the property is changed - whether through the animation system or via custom add-on code.

So howiem’s observation (above) about “modified” meaning “something the user does directly” appears to be the proper interpretation for the update function.

Here’s the updated version using get and set:

"""
This program demonstrates that the update function is not called
when a property is changed via the animation system.
"""

bl_info = {
    "version": "0.2",
    "name": "Update on Animate",
    'author': 'Bob',
    "location": "Properties > Scene",
    "category": "Blender Experiments"
    }


import bpy
from bpy.props import *


def get_y ( self ):
    print ( "y(%g) = %g" % (self.x,self.y) )
    return ( 2 * self.x )


class AppGroup(bpy.types.PropertyGroup):
    x = FloatProperty(name="x", default=1.0)
    y = FloatProperty(name="y", default=2.0, get=get_y)


class APP_PT_Props(bpy.types.Panel):
    bl_label = "App Properties"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "scene"
    def draw(self, context):
        app = context.scene.app
        row = self.layout.row()
        row.prop(app, "x", text="x")
        row = self.layout.row()
        row.prop(app, "y", text="y = 2*x")

def register():
    print ("Registering ", __name__)
    bpy.utils.register_module(__name__)
    bpy.types.Scene.app = bpy.props.PointerProperty(type=AppGroup)

def unregister():
    print ("Unregistering ", __name__)
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.app

if __name__ == "__main__":
    register()

Thanks for the help howiem !!