3
$\begingroup$

I want to synchronize two properties (e.g. sizeX and SizeY) when a “lock size” property is enabled. I have the properties SizeX and SizeY update callbacks linked to separate methods: update_sizex and update_sizey. When the property “lock sizes” is enabled, I want my update_sizex callback function to update the value of the property sizey whenever the user changes property sizex and the update_sizey callback function update the value of sizex whenever the user changes property sizey. Obviously since both properties have update callbacks, changing the value of one property programmatically from within the callback of the other property will create a cascade of recursive callbacks. Is there a way to temporarily disable the callback of a property programmatically just so that its value can be synced with other property value? (think of this as the Photoshop resize width / height lock mechanism).

sizex = FloatProperty(name='Size X', description='Size of plane along X',
                      default=10.0, update=update_sizex)

sizey = FloatProperty(name='Size Y', description='Size of plane along Y',
                      default=10.0, update=update_sizey)

def update_sizex(self, context):
        if self.lockSize:
            # update self.sizey without triggering recursive update callback

def update_sizey(self, context):
        if self.lockSize:
            # update self.sizex without triggering recursive update callback

synchronize property values

$\endgroup$
2
  • $\begingroup$ By synced, you mean you want them to have the same value, right?(just making sure) $\endgroup$
    – cmomoney
    Mar 13, 2017 at 14:26
  • $\begingroup$ or If sizex is 1 and sizey is 4 and locksize is enabled, I'd assume if x is resized to 2, sizey is automatically resized to 8. $\endgroup$
    – batFINGER
    Mar 13, 2017 at 14:54

3 Answers 3

4
$\begingroup$

Only trigger the update when it makes sense, when the value will be changed. Otherwise then don't set the value to the counterpart.

sizex = FloatProperty(name='Size X', description='Size of plane along X',
                      default=10.0, update=update_sizex)

sizey = FloatProperty(name='Size Y', description='Size of plane along Y',
                      default=10.0, update=update_sizey)

def update_sizex(self, context):
    if self.lockSize and self.sizey != self.sizex:
        self.sizey = self.sizex

def update_sizey(self, context):
    if self.lockSize and self.sizex != self.sizey:
        self.sizex = self.sizey

You can also use a temporary property for this. But in my opinion that makes things too complex. Also be aware that the setting can be set incorrectly making the lockSize functionality not working.

  • from the outside the do_not_update setting can be altered making the whole lockSize button broken.
  • when an exception happens during updating.

This can be fixed by a using finaly

try:
    self.do_not_update=True
    self.sizex = self.sizey

finally:
    self.do_not_update=False
$\endgroup$
3
$\begingroup$

You can simply write your update functions like this:

def update_sizex(self, context):
    if self.lockSize:
        # update self.sizey without triggering recursive update callback
        self["sizey"] = self.sizex

def update_sizey(self, context):
    if self.lockSize:
        # update self.sizex without triggering recursive update callback
        self["sizex"] = self.sizey

This won't call the update function when writing to the property.

$\endgroup$
0
$\begingroup$

You can simply test in the update function if the other value is different. If it is, you update.

EDIT:

I allow myself to edit my answer, sorry, I had misread the question.

For the example, we said that I modify the variable "value_x" and that the variable "lock_value" is activated.

At the beginning, I get the old value of "value_x" with the set function of FloatProperty to store it in a variable I called "old_value_x".

Then, the updating function of "value_x" is called. To prevent the update function from the "value_y" variable from working after the new value has been assigned, I use a boolean which, if true, will simply stop the update. I assign the new value to "value_y" by doing the calculation with the old "value_x" value stored previously and passing my boolean "lock_value" to false to find my function update for subsequent changes.

import bpy


def get_size_x(self):
    return bpy.context.scene.new_size_x

def set_size_x(self, value):
    bpy.context.scene.old_size_x = self.size_x
    bpy.context.scene.new_size_x = value
    if value == 0:
        bpy.context.scene.lock_size = False

def update_size_y(self, context):
    scn = context.scene

    if not scn.do_not_update:
        if scn.lock_size:
            scn.do_not_update = True 
            scn.size_y = scn.size_x / (scn.old_size_x / scn.size_y)
            scn.do_not_update = False



def get_size_y(self):
    return bpy.context.scene.new_size_y

def set_size_y(self, value):
    bpy.context.scene.old_size_y = self.size_y
    bpy.context.scene.new_size_y = value
    if value == 0:
        bpy.context.scene.lock_size = False

def update_size_x(self, context):
    scn = context.scene

    if not scn.do_not_update:
        if scn.lock_size:
            scn.do_not_update = True 
            scn.size_x = scn.size_y / (scn.old_size_y / scn.size_x)
            scn.do_not_update = False



class TestPanel(bpy.types.Panel):
    bl_idname = "test_panel"
    bl_label = "Test Panel"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = "category"

    def draw(self, context):
        scn = context.scene
        layout = self.layout
        row = layout.row(align = True)
        row.prop(scn, "size_x")
        lock_icon = 'LINKED' if scn.lock_size else 'UNLINKED'
        row.prop(scn, "lock_size", text = "", icon = lock_icon)
        row.prop(scn, "size_y")


def register():
    bpy.utils.register_module(__name__)

    bpy.types.Scene.size_x = bpy.props.FloatProperty(name = "Size X", get = get_size_x, set = set_size_x, update = update_size_y)
    bpy.types.Scene.old_size_x = bpy.props.FloatProperty()
    bpy.types.Scene.new_size_x = bpy.props.FloatProperty(default = 10.0)
    bpy.types.Scene.size_y = bpy.props.FloatProperty(name = "Size Y", get = get_size_y, set = set_size_y, update = update_size_x)
    bpy.types.Scene.old_size_y = bpy.props.FloatProperty()
    bpy.types.Scene.new_size_y = bpy.props.FloatProperty(default = 10.0)
    bpy.types.Scene.lock_size = bpy.props.BoolProperty(name = "Lock Value", default = False)
    bpy.types.Scene.do_not_update = bpy.props.BoolProperty(name = "Lock Value", default = False)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .