0
$\begingroup$

I've created a Blender addon, one of the functions (It creates a bunch of materials and handles visibility of frames, it's the update_materials function) only works the first time when you install the addon, when I open a new Blender file and try again, everything works but this function. Does anyone know why this would happen? Here's part of my code:

import bpy
import re


class StopMotionAddonPanel(bpy.types.Panel):
# Adding panel


class OBJECT_OT_CreateStopMo(bpy.types.Operator):
# Innitiate Stop Motion tool

class OBJECT_OT_NewFrame(bpy.types.Operator):
# Create new empty frame

class OBJECT_OT_DuplicateFrame(bpy.types.Operator):
# Duplicate frame

class OBJECT_OT_SetPosition(bpy.types.Operator):
# Set position

def menu_func(self, context):
    self.layout.operator("stopmotion.create_stopmo")
    self.layout.operator("stopmotion.new_frame")
    self.layout.operator("stopmotion.duplicate_frame")
    self.layout.operator("stopmotion.set_position")
    self.layout.prop(context.scene, "onion_skinning", text="Onion Skinning")
    self.layout.prop(context.scene, "frame_visibility", text="Frame Visibility")
    self.layout.prop(context.scene, "frame_selector", text="Frame Selector")


def update_materials(scene):
    frames_collection = bpy.data.collections.get("Frames")
    active_frame = bpy.context.active_object
    show_all_frames = bpy.context.scene.onion_skinning
    frame_visibility = bpy.context.scene.frame_visibility

    if active_frame:
        active_index = int(re.search(r'\d+', active_frame.name).group())
    else:
        active_index = 0

    # Clear materials for all frames
    for obj in frames_collection.objects:
        obj.data.materials.clear()

    # Assign materials to the frames based on distance from the active frame
    material_name_base = "Transparent Frame"
    transparency_values = [0.8, 0.6, 0.4, 0.2, 0.0]

    for obj in frames_collection.objects:
        frame_index = int(re.search(r'\d+', obj.name).group())
        distance = abs(frame_index - active_index)

        if distance == 0:
            material_name = "Current Frame"
            material = bpy.data.materials.get(material_name)
            if material is None:
                material = bpy.data.materials.new(name=material_name)
            obj.data.materials.append(material)
        elif distance <= len(transparency_values) and show_all_frames:
            alpha = transparency_values[distance - 1]
            material_name = f"{material_name_base} {alpha}"
            material = bpy.data.materials.get(material_name)
            if material is None:
                material = bpy.data.materials.new(name=material_name)
                material.use_nodes = False
                material.diffuse_color = (1.0, 0.0, 0.0, 1.0)
                material.alpha = alpha
            obj.data.materials.append(material)
            for slot in obj.material_slots:
                if slot.material == material:
                    slot.material.diffuse_color[3] = alpha
        elif show_all_frames or obj == active_frame:
            material_name = f"{material_name_base} 0.0"
            material = bpy.data.materials.get(material_name)
            if material is None:
                material = bpy.data.materials.new(name=material_name)
                material.use_nodes = False
                material.diffuse_color = (1.0, 0.0, 0.0, 1.0)
                material.alpha = 0.0
            obj.data.materials.append(material)
            for slot in obj.material_slots:
                if slot.material == material:
                    slot.material.diffuse_color[3] = 0.0

    # Set frame visibility in the viewport
    for obj in frames_collection.objects:
        obj.hide_set(not frame_visibility)
        obj.hide_viewport = not frame_visibility

    # Ensure the active frame is always visible
    if active_frame:
        active_frame.hide_set(False)
        active_frame.hide_viewport = False


def update_frame_selector(self, context):
# Frame selector tool

def register():
    bpy.types.Scene.onion_skinning = bpy.props.BoolProperty(
        name="Onion Skinning",
        description="Toggle onion skinning for frames",
        default=True
    )
    bpy.types.Scene.frame_visibility = bpy.props.BoolProperty(
        name="Frame Visibility",
        description="Toggle visibility of frames in the viewport",
        default=True
    )
    bpy.types.Scene.frame_selector = bpy.props.IntProperty(
        name="Frame Selector",
        description="Select active frame by number",
        default=1,
        min=1,
        update=update_frame_selector
    )

    bpy.utils.register_class(StopMotionAddonPanel)
    bpy.utils.register_class(OBJECT_OT_CreateStopMo)
    bpy.utils.register_class(OBJECT_OT_NewFrame)
    bpy.utils.register_class(OBJECT_OT_DuplicateFrame)
    bpy.utils.register_class(OBJECT_OT_SetPosition)
    bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
    bpy.app.handlers.depsgraph_update_post.append(update_materials)  # Added this line


def unregister():
    bpy.utils.unregister_class(StopMotionAddonPanel)
    bpy.utils.unregister_class(OBJECT_OT_CreateStopMo)
    bpy.utils.unregister_class(OBJECT_OT_NewFrame)
    bpy.utils.unregister_class(OBJECT_OT_DuplicateFrame)
    bpy.utils.unregister_class(OBJECT_OT_SetPosition)
    bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
    bpy.app.handlers.depsgraph_update_post.remove(update_materials)  # Added this line


if __name__ == "__main__":
    register()

Im pretty sure it has something to do with registering and deregistering, any help is greatly appreciated!

$\endgroup$

1 Answer 1

2
$\begingroup$

I suspect it is because handlers are freed when loading a new file by default. Sometimes it is useful to keep handlers between files. To do this you may add a persistent decorator before declaring your function like shown below:

from bpy.app.handlers import persistent

@persistent
def update_materials(scene):

for more info on handlers, you may read the documentation found here Application Handlers (bpy.app.handlers)

$\endgroup$
3
  • $\begingroup$ That seems to work like a charm, thanks! $\endgroup$
    – OnnoVader
    Nov 23, 2023 at 12:24
  • $\begingroup$ No worries, if this solves your issue then would you mind marking this question answered $\endgroup$ Nov 23, 2023 at 12:34
  • $\begingroup$ @OnnoVader Please don't reply "Thanks" as an answer or comment, instead upvote the post that helped you. If you feel it completely solved your issue consider marking it as accepted. $\endgroup$ Nov 23, 2023 at 19:17

You must log in to answer this question.

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