Changes: - Adapted to work with version 3.3.2 SPA Studios current version. - Added a reload guard in `__init__.py` to dynamically reload submodules if they are already loaded. This ensures that changes to submodule code are reflected without needing to restart Blender. - Modified the `register` and `unregister` functions in `sync/core.py` and `utils.py` to include try-except blocks around class registration and unregistration calls. This prevents errors when attempting to register a class that's already registered or unregister a class that's not. - Added debug print statements across `register` and `unregister` functions to provide clear feedback in the console about the addon's loading state, especially useful during development. - Updated `gpu_utils.py` to use the correct shader `2D_UNIFORM_COLOR` and added comments regarding the use of color attributes in the `OverlayDrawer` class.
+97 −30 · 4 files changed
-
spa_sequencer/__init__.py+30 −5 -
spa_sequencer/gpu_utils.py+4 −1 -
spa_sequencer/sync/core.py+47 −21 -
spa_sequencer/utils.py+16 −3
@@ -1,6 +1,29 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (C) 2023, The SPA Studios. All rights reserved. +import bpy +import importlib + +# List of submodule names as strings +submodules = [ + "editorial", + "keymaps", + "preferences", + "render", + "sequence", + "shared_folders", + "shot", + "sync", +] + +# Reload guard +for submodule in submodules: + if submodule in locals(): + importlib.reload(locals()[submodule]) + else: + exec(f"from . import {submodule}") + +# Now, you can safely reference the modules directly from spa_sequencer import ( editorial, keymaps, @@ -12,7 +35,6 @@ from spa_sequencer import ( sync, ) - bl_info = { "name": "Sequencer", "author": "The SPA Studios", @@ -24,7 +46,6 @@ bl_info = { "category": "SPA", } - packages = ( sync, shot, @@ -36,12 +57,16 @@ packages = ( keymaps, ) - def register(): + print("Starting addon registration") for package in packages: + print(f"Registering package: {package.__name__}") package.register() - + print("Addon registration completed") def unregister(): - for package in packages: + print("Starting addon unregistration") + for package in reversed(packages): + print(f"Unregistering package: {package.__name__}") package.unregister() + print("Addon unregistration completed") @@ -22,11 +22,14 @@ class OverlayDrawer: def __init__(self): self.format = gpu.types.GPUVertFormat() - self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") + # Change "UNIFORM_COLOR" to "2D_UNIFORM_COLOR" + self.shader = gpu.shader.from_builtin("2D_UNIFORM_COLOR") self.pos_id = self.format.attr_add( id="pos", comp_type="F32", len=2, fetch_mode="FLOAT" ) + # Since you're using a uniform color, you might not need to add a color attribute to the format. + # However, if you plan to use per-vertex colors, then keep this line. self.color_id = self.format.attr_add( id="color", comp_type="F32", len=2, fetch_mode="FLOAT" ) @@ -634,32 +634,58 @@ classes = (TimelineSyncSettings,) def register(): - register_classes(classes) + # Development-only: Forcefully unregister class if already registered + # This is a safeguard and should not be necessary in production + try: + bpy.utils.unregister_class(TimelineSyncSettings) + except Exception as e: + print(f"Development-only: Could not unregister TimelineSyncSettings: {e}") + print("Registering sync/core components") + bpy.utils.register_class(TimelineSyncSettings) + # Store Synchronization settings on the WindowManager type - # NOTE: this is not saved in the Blender file - bpy.types.WindowManager.timeline_sync_settings = bpy.props.PointerProperty( - type=TimelineSyncSettings, - name="Timeline Synchronization Settings", - ) - + if not hasattr(bpy.types.WindowManager, 'timeline_sync_settings'): + bpy.types.WindowManager.timeline_sync_settings = bpy.props.PointerProperty( + type=TimelineSyncSettings, + name="Timeline Synchronization Settings", + ) + # React to scenes current frame changes - bpy.app.handlers.frame_change_post.append(on_frame_changed) + if on_frame_changed not in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.append(on_frame_changed) # React to file opening - bpy.app.handlers.load_pre.append(on_load_pre) - bpy.app.handlers.load_post.append(on_load_post) + if on_load_pre not in bpy.app.handlers.load_pre: + bpy.app.handlers.load_pre.append(on_load_pre) + if on_load_post not in bpy.app.handlers.load_post: + bpy.app.handlers.load_post.append(on_load_post) - bpy.app.handlers.undo_post.append(on_undo_redo) - bpy.app.handlers.redo_post.append(on_undo_redo) + if on_undo_redo not in bpy.app.handlers.undo_post: + bpy.app.handlers.undo_post.append(on_undo_redo) + if on_undo_redo not in bpy.app.handlers.redo_post: + bpy.app.handlers.redo_post.append(on_undo_redo) def unregister(): - unregister_classes(classes) - - del bpy.types.WindowManager.timeline_sync_settings - bpy.app.handlers.frame_change_post.remove(on_frame_changed) - bpy.app.handlers.load_pre.remove(on_load_pre) - bpy.app.handlers.load_post.remove(on_load_post) - - bpy.app.handlers.undo_post.remove(on_undo_redo) - bpy.app.handlers.redo_post.remove(on_undo_redo) + print("Unregistering sync/core components") + try: + bpy.utils.unregister_class(TimelineSyncSettings) + except RuntimeError as e: + print(f"Could not unregister TimelineSyncSettings: {e}") + + if hasattr(bpy.types.WindowManager, 'timeline_sync_settings'): + del bpy.types.WindowManager.timeline_sync_settings + + # Remove handlers safely + if on_frame_changed in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.remove(on_frame_changed) + if on_load_pre in bpy.app.handlers.load_pre: + bpy.app.handlers.load_pre.remove(on_load_pre) + if on_load_post in bpy.app.handlers.load_post: + bpy.app.handlers.load_post.remove(on_load_post) + if on_undo_redo in bpy.app.handlers.undo_post: + bpy.app.handlers.undo_post.remove(on_undo_redo) + if on_undo_redo in bpy.app.handlers.redo_post: + bpy.app.handlers.redo_post.remove(on_undo_redo) + + print("sync/core components unregistered") @@ -77,12 +77,19 @@ def register_classes(classes: tuple[BlenderTypeClass, ...]): continue for c in classes: - bpy.utils.register_class(c) + try: + bpy.utils.register_class(c) + except ValueError as e: + # This assumes that the ValueError is due to the class already being registered + print(f"Warning: Attempted to register class {c.__name__} that might already be registered. {e}") + except Exception as e: + print(f"Error: Failed to register class {c.__name__}. {e}") + if issubclass(c, (bpy.types.Operator, bpy.types.Macro)): try: register_operator_keymaps(c) except Exception as e: - log.warning(f"Failed to register {c.bl_idname} keymaps: {e}") + print(f"Warning: Failed to register {c.bl_idname} keymaps: {e}") def unregister_classes(classes: tuple[BlenderTypeClass, ...], reverse=True): @@ -91,7 +98,13 @@ def unregister_classes(classes: tuple[BlenderTypeClass, ...], reverse=True): :param classes: the classes to unregister """ for c in reversed(classes) if reverse else classes: - bpy.utils.unregister_class(c) + try: + bpy.utils.unregister_class(c) + except ValueError as e: + # This assumes that the ValueError is due to the class not being registered + print(f"Warning: Attempted to unregister class {c.__name__} that might not be registered. {e}") + except Exception as e: + print(f"Error: Failed to unregister class {c.__name__}. {e}") def remove_auto_numbering_suffix(name: str) -> str: