3
$\begingroup$

Any ideas why the subscription via msgbus to property changes (node/attr) are dropped while starting a new blend or reloading a blend file?

I use msgbus to subscribe to changes to a node name (in the sverchok blender add-on) and I setup these subscriptions once in the register() of one of my nodes that need to monitor such changes. Everything works fine after I just launch Blender.. however, once I reload a saved blender file or create new blend the subscription does not work anymore. Reloading the scripts fixes the problem (since the subscription call is re-run).

(see here a video version of this description: https://youtu.be/-KwFKuxbnJs)

    VIZ_NODE1 = sverchok.nodes.viz.vd_draw_experimental.SvVDExperimental
    VIZ_NODE2 = sverchok.nodes.viz.viewer_idx28.SvIDXViewer28

    subscribe_to_list = [(VIZ_NODE1, "name"), (VIZ_NODE2, "name")]

    def msgbus_callback(*args):
        message = args[0]
        print("msgbus_callback: ", message)
        # propagate the messsasge to all layer nodes
        layer_nodes = [n for n in bpy.data.node_groups['NodeTree'].nodes if n.bl_idname == "SvViewerLayerNode"]
        for node in layer_nodes:
            print("propagate msgbus callback to:", node.bl_idname)
            node.viewer_changed_name()

    owner = object()

    def subscribe_to_viewer_changes(subscribe_to):
        # listen for changes to the "name" property of any node of viewer classes
        print("layer_nodes_subscribe_to_viewer_changes: ", subscribe_to)
        bpy.msgbus.subscribe_rna(
            key=subscribe_to,
            owner=owner,
            args=("Viewer node name property has changed",),
            notify=msgbus_callback,
        )

    def layer_nodes_subscribe_to_viewer_nodes_name_changes():
        print("layer_nodes_subscribe_to_viewer_changes")
        for subscribe_to in subscribe_to_list:
            subscribe_to_viewer_changes(subscribe_to)

   ...

    def register():
        print("Registering the SvViewerLayerNode classes")
        _ = [bpy.utils.register_class(cls) for cls in classes]

        layer_nodes_subscribe_to_viewer_nodes_name_changes()

```
$\endgroup$
2
  • $\begingroup$ There should be a persistent option that can be passed to keep the subscription when loading a new file. Will have to check. $\endgroup$ Commented Apr 26, 2020 at 7:46
  • $\begingroup$ This has to be done with a persistent application handler, the "PERSISTENT" option for msgbus has a different meaning. $\endgroup$ Commented Apr 26, 2020 at 10:35

1 Answer 1

1
$\begingroup$

The bpy.msgbus.subscribe_rna() is not persistent and will be removed when loading a new file. A persistent application handler, can be used to subscribe to the message bus when a new file is loaded. The example code below demonstrates this by appending a persistent handler load_handler to bpy.app.handlers.load_post.

This answer also contains a more detailed example of msgbus in an add-on.

import bpy
from bpy.app.handlers import persistent


# For more information see source/blender/python/intern/bpy_msgbus.c
# and https://developer.blender.org/P563


# Object that will store the handle to the msgbus subscription
subscription_owner = object()


def notification_handler(*args):
    print(f"Object: {bpy.context.object.name}, Location: {bpy.context.object.location}, Args: {args}")


def subscribe_to_active_obj():
    # What the subscription is for, in this example the active object.
    # Note that for properties it may be necessary to use `path_resolve`
    # as shown in https://developer.blender.org/P563
    subscribe_to = bpy.types.LayerObjects, "active"

    # Subscribe to the msgbus to call the handler when the key is modified.
    # Please note that not every UI interaction will publish an update,
    # despite modifying the key you've subscribed to. For instance, when
    # subscribed to `bpy.types.Object, "location"`, changing the location of 
    # an object through the move tool won't call the handler automatically, 
    # while updating the location property in the "Tranform" panel in the 
    # sidebar will. The publishing of an update can be forced through 
    # `bpy.msgbus.publish_rna()`.
    # The "PERSISTENT" option ensures that the subscriber remains registered
    # when remapping ID data, it does not mean that the subscription remains 
    # persistent when loading a different file. This can be accomplished with
    # a persistent application handler that is called on file load.
    bpy.msgbus.subscribe_rna(
        key=subscribe_to,
        owner=subscription_owner,
        args=("a", "b", "c"),
        notify=notification_handler,
        options={"PERSISTENT",}
    )


@persistent
def load_handler(dummy):
    subscribe_to_active_obj()


# Register the handler, ensures that the subscription will happen when a new 
# file is loaded.
bpy.app.handlers.load_post.append(load_handler)

# Subscribe to the active object for the current file.
subscribe_to_active_obj()

## Explicitly notify subscribers of changes to this property
# bpy.msgbus.publish_rna(key=subscribe_to)

## Clear all subscribers by this owner
# bpy.msgbus.clear_by_owner(subscription_owner)
$\endgroup$

You must log in to answer this question.

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