Execute custom script when object is moved

I would like to be able to execute a custom script every time a particular object is moved in the viewport. Is there a clean way to do this?

I am using an empty object to guide deformation of a mesh. Right now, I move the empty, and then call my custom operator (by a custom defined keystroke). I’d really like it to work at interactive rates though.

I know that there is an “update” callback function that gets called for custom parameters. But I don’t see how to make that work for a built-in parameter like “location”.

Have you considered drivers?

A driver at this range??

But seriously, I had not considered drivers. I will look into that possibility more closely.

It seems a bit strange to use a driver, because there no particular single parameter that I want to drive, I just want to be able to auto-run a script. (Like printing text the console or something, every time the object is moved.) But maybe I could set up a dummy parameter, like an irrelevant color, and in the process of “driving” that, my script will execute.

Thanks for the lead.

I think you have to use “modal operator” for that kind of operations. A modal operator keeps running a code in the background that you specify.
Here is a file toggle_bones.blend (73.8 KB) that Liero did for a question I made .
That may help you.
Also check this help for more info about modal operator.

When you jump into this realm, you have to consider renderability. A modal operator, while working in the GUI, might not actually work when rendering your animation. You lose the context when you render. Drivers would be my first choice.

Thanks for the ideas. I’ll post back when I figure out what works best.

On reflection, a handler may be the option you are after. Below is a simple example.


import bpy

data = {}  #keep data in a dictionary
TOL = 1.0e-4 #how much the cube needs to move in a frame

def loc_change(scene):
    # run some scripts if the cube has moved.
    cube = scene.objects.get("Cube")
    loc = cube.location
    
    dloc = loc - data["CubeLoc"]
    
    
    if dloc.length > TOL:
        print("FRAME:%d Cube has moved" % scene.frame_current)
        data["CubeLoc"] = loc.copy()
        
bpy.app.handlers.frame_change_pre.append(loc_change)

# initialise data
data["CubeLoc"] = bpy.data.objects.get("Cube").location.copy()

3 Likes

@batFinger: That will work when the frame changes. In the first post the user is looking for an interactive way to call the update routine. When I create my own custom properties I can point them to an update def. I am wondering if there is a way to hi-jack a standard property to do the same thing? That way we could point a change in object location to our own update call, or your loc_change without having to change the frame at all.

Oh ok… change to scene_update_pre handler instead.

With the update, you kinda need to fire that event by changing the prop via the UI or a script. I use that method to make a UI have a live refresh by simply calling x.prop = x.prop in a handler. If i drive that prop to change it the update event isn’t called. Trying to do too much with updates causes grief in my experience.

Handlers FTW. It’s working absolutely perfectly.

In the end, I needed to use “scene_update_post”. When using pre-updating, I got foiled by recursion, because my script modifies the scene, which then invokes my script again, which modifies the scene again, etc.

Here’s a (temporary) link to show what I was able to do with this. I’m working on constraint-based deformation (a.k.a. direct manipulation FFD).

I would be very interested in exactly the same thing. It looks like scene_update_post and scene_update_pre don’t exist anymore though. Anyone know how to do this seemingly simple thing?

Those handlers were deprecated, use the depsgraph update handlers instead.