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$ – Robert Gützkow Apr 26 '20 at 7:46
  • $\begingroup$ This has to be done with a persistent application handler, the "PERSISTENT" option for msgbus has a different meaning. $\endgroup$ – Robert Gützkow Apr 26 '20 at 10:35
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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.