How to get location offsets before get/set

Hello,
I’m trying to move multiple objects in location.x with a Float Slider. In order to do that, I’m currently trying to use a getter & setter for the active object and then find the difference between the active object’s & other selected objects’ locations. This is what I have so far.

import bpy,math

def get_location_x(self):
    return bpy.context.active_object.location.x

def set_location_x(self,location_x):
    active_object = bpy.context.active_object
    for obj in bpy.context.selected_objects:
        if obj == active_object:
            obj.location.x = location_x
        else:
            offset_x = active_object.location.x-obj.location.x
            print("offset_x: %s"%offset_x)
            obj.location.x = location_x+offset_x
    
class ExamplePanel(bpy.types.Panel):
    bl_idname = "VIEW3D_PT_MoveXPanel"
    bl_label = "Move X"
    bl_space_type = "VIEW_3D"
    bl_category = "Move X"
    bl_region_type = "UI"
    
    def draw(self, context):
        col = self.layout.column()
        for (prop_name, _) in PROPS:
            row = col.row()
            row.prop(context.scene, prop_name)

PROPS = [
    ("location_x", bpy.props.FloatProperty(name="Location X",
        get = get_location_x, set = set_location_x
    ))
]

def register():
    for (prop_name, prop_value) in PROPS:
        setattr(bpy.types.Scene, prop_name, prop_value)
    bpy.utils.register_class(ExamplePanel)

def unregister():
    for (prop_name, _) in PROPS:
        delattr(bpy.types.Scene, prop_name)
    bpy.utils.unregister_class(ExamplePanel)

if __name__ == '__main__':
    register()

Script Demo
The other objects’ offset_x is flickering from negative to positive. Also, offset_x value is incorrect by the time set_location_x is run because it doesn’t seem to get fired until the value changes. How can I get my objects to move together while keeping their original offset? Thank you!

don’t have time to test your code but object.location is a deferred update, meaning it does not happen immediately when you set the value. For instantaneous updates it’s better to adjust the world matrix directly using object.world_matrix.translation. Not sure if this is causing your issue or not but that would be my first suspicion

Thank you for the reply! I tried matrix_world.translation.x, but it did not fix the issue.

You can’t modify the active object’s location and compute the difference in the same loop.

Store the old value first. Also, flip the operands in offset_x.

def set_location_x(self,location_x):
    active_object = bpy.context.active_object

    act_prev_x = active_object.location.x
    active_object.location.x = location_x
    
    for obj in set(bpy.context.selected_objects) - {active_object}:
        offset_x = obj.location.x - act_prev_x
        obj.location.x = offset_x + location_x

That worked! Thank you! :grinning: