5

I've tried to find a way to do this using handlers and events, etc. Essentially I need to know when an object is selected. This isn't a click event, or another action based on user input, but instead based on user selection.

I guess I could capture all clicks through a modal and use that to check selected objects, but that seems like overkill. Blender knows when something has been selected. I just don't know how to piggy back off of that.

EDIT

This is the modified script from the examples I could find. While I want selection eventually, for now just wanted to get something that would work. Anyone know why this script won't work?

import bpy

ob = bpy.data.objects['Cube']
def cube_location_callback(ob):
    # Do something here
    print("Cube has moved to ", ob.location)


def subscribe_to_cube_loc_change(ob):
    if ob.type != 'MESH':
        return

    subscribe_to = bpy.context.object.location

    bpy.msgbus.subscribe_rna(
        key=subscribe_to,
        # owner of msgbus subcribe (for clearing later)
        owner=ob,
        # Args passed to callback function (tuple)
        args=(ob,),
        # Callback function for property update
        notify=cube_location_callback,
    )

subscribe_to_cube_loc_change(ob)

FOLLOW UP

Since my original goal was to fire an even upon object selection, I wanted to update with a solution.

subscribe_to = bpy.types.LayerObjects, "active"
bpy.msgbus.subscribe_rna(
    key = subscribe_to,
    owner = self,
    args = (self,),
    notify = wall_selected_callback,
    )

This taps into the active object selector and will callback whenever the active object changes. This is pretty handy.

It's not what I wanted originally, which was the ability to tie back into the object itself and only fire when that object is selected. If that were available then the object would automatically know what to do when selected. With this solution the script still needs to figure out what has been selected and what the next actions is. There may still be a way to register with the object itself, but I haven't got that far yet.

A lot of people recommend using handlers for this type of usage, but the only option for this is the bpy.app.handlers.depsgraph_update_pre/post command. This can fire off a lot of extra events if all you want is to know when an object has been selected. Plus handlers seem to revolve much more around file and animation operations. Using the above is a more targeted solution.

Sam Vimes
  • 241
  • 3
  • 14
  • https://devtalk.blender.org/t/addon-development-question-attaching-callback-to-buttons/8677/2 – Blender Dadaist Sep 05 '19 at 09:17
  • Interesting... not heard of the msgbus but seems like the right area to catch events. Also looks like a bit of dark magic involved and not sure if the examples out there still work for 2.8. I edited my post with the code I tried but can't get to work yet. – Sam Vimes Sep 05 '19 at 17:02

2 Answers2

11

You have to pass "location" (string) to path_resolve():

import bpy

# Callback function for location changes
def obj_location_callback(ob):
    # Do something here
    print('Object "{}" changed its location to: {}: '.format(
        ob.name, ob.location)
        )


# Subscribe to the context object (mesh)
def subscribe_to_obj_loc(ob):
    if ob.type != 'MESH':
        return

    subscribe_to = ob.path_resolve("location", False)

    bpy.msgbus.subscribe_rna(
        key=subscribe_to,
        # owner of msgbus subcribe (for clearing later)
        owner=ob,
        # Args passed to callback function (tuple)
        args=(ob,),
        # Callback function for property update
        notify=obj_location_callback,
    )

# Ensure only meshes are passed to this function
subscribe_to_obj_loc(bpy.context.object)
brockmann
  • 12,613
  • 4
  • 50
  • 93
  • Hi. This is very interesting (did not know that 'msgbus'). That means that all that have rna path can benefit for an update callback, that's it? Is there some documentation somewhere or described roadmap about it? Thanks. And additionally 'owner' is the object? I mean clear will clear all callbacks? – lemon Sep 05 '19 at 17:48
  • 2
    New to me too @lemon :P ... Yep looks like that. Still on it and try to figure out how to get rid of it by using bpy.msgbus.clear_by_owner(ob), also I don't get why it doesn't work when grabbing the object... – brockmann Sep 05 '19 at 18:17
  • 1
    Found a more complete example using a 'handle': https://developer.blender.org/P563 @lemon – brockmann Sep 05 '19 at 20:11
  • I've updated script the reflect the changes, which appears to be the correct answer. However I can't get my script to report any feedback. I assume just by moving the object it should callback the location? – Sam Vimes Sep 06 '19 at 17:51
  • To make it work, run it, bring up the N-Panel and drag any location slider... What do you think is wrong (not correct) on my answer? The only difference I see is that you are not using ob.path_resolve(loc... but this returns the python path of the property (loc) so it's even safer for general usage (other properties) at the moment. @SamVimes – brockmann Sep 06 '19 at 18:28
  • @brockmann Your answer is correct... didn't mean to imply otherwise. My only comment there was that the answer looked correct even though I didn't know how to use it correctly. Once I slid the object around using the N-Panel everything worked. I had originally thought it would also work by the Move function, so that's why I was confused. Thanks for the help! – Sam Vimes Sep 06 '19 at 21:28
  • No problem @SamVimes. There is indeed a lot of confusion about this, see lemons' comment... It looks weird and unfinished. For e.g. I'm still not sure how to 'unsubscribe' properly. Have you already seen this? Anyway, I promise that in case I find out something new to it, I'll update my answer... That's all what I can do. Cheers! – brockmann Sep 06 '19 at 21:42
3

If I understand your question correctly, I can offer the following solution:

import bpy

def msgbus_callback(*arg): # in console will be print selected_objects print(bpy.context.selected_objects) # you can do something

def subscribe_to_obj():

bpy.msgbus.subscribe_rna(
    key=(bpy.types.LayerObjects, 'active'),
    owner=bpy,
    args=('something, if you need',),
    notify=msgbus_callback
    )

if name == "main": subscribe_to_obj()

Olg
  • 31
  • 2