feat: Added handler bubble-up logic.

uv-refactor
Sofus Albert Høgsbro Rose 2024-10-02 11:24:48 +02:00
parent 12362b3cbd
commit 55235c7032
Signed by: so-rose
GPG Key ID: AD901CB0F3701434
7 changed files with 198 additions and 7 deletions

View File

@ -16,7 +16,10 @@
"""A visual DSL for electromagnetic simulation design and analysis implemented as a Blender node editor.""" """A visual DSL for electromagnetic simulation design and analysis implemented as a Blender node editor."""
# from . import assets, node_trees, operators, preferences, registration from functools import reduce
# from . import node_trees, operators, preferences, registration
from . import assets, preferences, registration
from . import contracts as ct from . import contracts as ct
from .utils import logger from .utils import logger
@ -32,6 +35,17 @@ BL_REGISTER: list[ct.BLClass] = [
# *node_trees.BL_REGISTER, # *node_trees.BL_REGISTER,
] ]
BL_HANDLERS: ct.BLHandlers = reduce(
lambda a, b: a + b,
[
assets.BL_HANDLERS,
# *operators.BL_HANDLERS,
# *assets.BL_HANDLERS,
# *node_trees.BL_HANDLERS,
],
ct.BLHandlers(),
)
BL_HOTKEYS: list[ct.KeymapItemDef] = [ BL_HOTKEYS: list[ct.KeymapItemDef] = [
# *operators.BL_HOTKEYS, # *operators.BL_HOTKEYS,
# *assets.BL_HOTKEYS, # *assets.BL_HOTKEYS,
@ -64,6 +78,7 @@ def register() -> None:
log.info('Registering Addon: %s', ct.addon.NAME) log.info('Registering Addon: %s', ct.addon.NAME)
registration.register_classes(BL_REGISTER) registration.register_classes(BL_REGISTER)
registration.register_handlers(BL_HANDLERS)
registration.register_hotkeys(BL_HOTKEYS) registration.register_hotkeys(BL_HOTKEYS)
log.info('Finished Registration of Addon: %s', ct.addon.NAME) log.info('Finished Registration of Addon: %s', ct.addon.NAME)
@ -83,7 +98,8 @@ def unregister() -> None:
""" """
log.info('Starting %s Unregister', ct.addon.NAME) log.info('Starting %s Unregister', ct.addon.NAME)
registration.unregister_classes()
registration.unregister_hotkeys() registration.unregister_hotkeys()
registration.unregister_handlers()
registration.unregister_classes()
log.info('Finished %s Unregister', ct.addon.NAME) log.info('Finished %s Unregister', ct.addon.NAME)

View File

@ -14,17 +14,30 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from functools import reduce
import oscillode.contracts as ct
from . import geonodes from . import geonodes
BL_REGISTER = [ BL_REGISTER: list[ct.BLClass] = [
*geonodes.BL_REGISTER, *geonodes.BL_REGISTER,
] ]
BL_HOTKEYS = [ BL_HANDLERS: ct.BLHandlers = reduce(
lambda a, b: a + b,
[
geonodes.BL_HANDLERS,
],
ct.BLHandlers(),
)
BL_HOTKEYS: list[ct.KeymapItemDef] = [
*geonodes.BL_HOTKEYS, *geonodes.BL_HOTKEYS,
] ]
__all__ = [ __all__ = [
'BL_REGISTER', 'BL_REGISTER',
'BL_HANDLERS',
'BL_HOTKEYS', 'BL_HOTKEYS',
] ]

View File

@ -579,11 +579,11 @@ def initialize_asset_libraries(_: bpy.types.Scene):
) )
bpy.app.handlers.load_pre.append(initialize_asset_libraries)
BL_REGISTER = [ BL_REGISTER = [
NodeAssetPanel, NodeAssetPanel,
GeoNodesToStructureNode, GeoNodesToStructureNode,
] ]
BL_HOTKEYS = [] BL_HANDLERS: ct.BLHandlers = ct.BLHandlers(load_pre=(initialize_asset_libraries,))
BL_HOTKEYS: list[ct.KeymapItemDef] = []

View File

@ -39,6 +39,7 @@ from .bl import (
PropName, PropName,
SocketName, SocketName,
) )
from .bl_handlers import BLHandlers
from .icons import Icon from .icons import Icon
from .mobj_types import ManagedObjType from .mobj_types import ManagedObjType
from .node_tree_types import ( from .node_tree_types import (
@ -74,6 +75,7 @@ __all__ = [
'PresetName', 'PresetName',
'PropName', 'PropName',
'SocketName', 'SocketName',
'BLHandlers',
'Icon', 'Icon',
'BLInstance', 'BLInstance',
'InstanceID', 'InstanceID',

View File

@ -0,0 +1,122 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Declares types for working with `bpy.app.handlers` callbacks."""
import typing as typ
import bpy
import pydantic as pyd
from oscillode.utils.staticproperty import staticproperty
BLHandler = typ.Callable[[], None]
BLHandlerWithFile = typ.Callable[[str], None]
BLHandlerWithRenderStats = typ.Callable[[typ.Any], None]
class BLHandlers(pyd.BaseModel):
"""Contains lists of handlers associated with this addon."""
animation_playback_post: tuple[BLHandler, ...] = ()
animation_playback_pre: tuple[BLHandler, ...] = ()
annotation_post: tuple[BLHandler, ...] = ()
annotation_pre: tuple[BLHandler, ...] = ()
composite_cancel: tuple[BLHandler, ...] = ()
composite_post: tuple[BLHandler, ...] = ()
composite_pre: tuple[BLHandler, ...] = ()
depsgraph_update_post: tuple[BLHandler, ...] = ()
depsgraph_update_pre: tuple[BLHandler, ...] = ()
frame_change_post: tuple[BLHandler, ...] = ()
frame_change_pre: tuple[BLHandler, ...] = ()
load_factory_preferences_post: tuple[BLHandler, ...] = ()
load_factory_startup_post: tuple[BLHandler, ...] = ()
load_post: tuple[BLHandlerWithFile, ...] = ()
load_post_fail: tuple[BLHandlerWithFile, ...] = ()
load_pre: tuple[BLHandlerWithFile, ...] = ()
object_bake_cancel: tuple[BLHandler, ...] = ()
object_bake_complete: tuple[BLHandler, ...] = ()
object_bake_pre: tuple[BLHandler, ...] = ()
redo_post: tuple[BLHandler, ...] = ()
redo_pre: tuple[BLHandler, ...] = ()
render_cancel: tuple[BLHandler, ...] = ()
render_complete: tuple[BLHandler, ...] = ()
render_init: tuple[BLHandler, ...] = ()
render_post: tuple[BLHandler, ...] = ()
render_pre: tuple[BLHandler, ...] = ()
render_stats: tuple[BLHandler, ...] = ()
####################
# - Properties
####################
@staticproperty # type: ignore[arg-type]
def hander_categories() -> tuple[str, ...]: # type: ignore[misc]
"""Returns an immutable string sequence of handler categories."""
return (
'animation_playback_post',
'animation_playback_pre',
'annotation_post',
'annotation_pre',
'composite_cancel',
'composite_post',
'composite_pre',
'depsgraph_update_post',
'depsgraph_update_pre',
'frame_change_post',
'frame_change_pre',
'load_factory_preferences_post',
'load_factory_startup_post',
'load_post',
'load_post_fail',
'load_pre',
'object_bake_cancel',
'object_bake_complete',
'object_bake_pre',
'redo_post',
'redo_pre',
'render_cancel',
'render_complete',
'render_init',
'render_post',
'render_pre',
'render_stats',
)
####################
# - Merging
####################
def __add__(self, other: typ.Self) -> typ.Self:
"""Concatenate the handlers of two `BLHandlers` objects."""
return BLHandlers(
**{
hndl_cat: getattr(self, hndl_cat) + getattr(self, hndl_cat)
for hndl_cat in self.handler_categories
}
)
def register(self) -> None:
"""Registers all handlers declared by-category."""
for handler_category in BLHandlers.handler_categories:
for handler in getattr(self, handler_category):
getattr(bpy.app.handlers, handler_category).append(handler)
def unregister(self) -> None:
"""Unregisters only this addon's handlers from bpy.app.handlers."""
for handler_category in BLHandlers.handler_categories:
for handler in getattr(self, handler_category):
bpy_handlers = getattr(bpy.app.handlers, handler_category)
if handler in bpy_handlers:
bpy_handlers.remove(handler)

View File

@ -33,9 +33,12 @@ log = logger.get(__name__)
# - Globals # - Globals
#################### ####################
_REGISTERED_CLASSES: list[ct.BLClass] = [] _REGISTERED_CLASSES: list[ct.BLClass] = []
_ADDON_KEYMAP: bpy.types.KeyMap | None = None _ADDON_KEYMAP: bpy.types.KeyMap | None = None
_REGISTERED_HOTKEYS: list[ct.BLKeymapItem] = [] _REGISTERED_HOTKEYS: list[ct.BLKeymapItem] = []
_REGISTERED_HANDLERS: ct.BLHandlers | None = None
#################### ####################
# - Class Registration # - Class Registration
@ -74,6 +77,35 @@ def unregister_classes() -> None:
_REGISTERED_CLASSES.clear() _REGISTERED_CLASSES.clear()
####################
# - Handler Registration
####################
def register_handlers(bl_handlers: ct.BLHandlers) -> None:
"""Register the given Blender handlers."""
global _REGISTERED_HANDLERS # noqa: PLW0603
log.info('Registering BLHandlers') ## TODO: More information
if _REGISTERED_HANDLERS is None:
bl_handlers.register()
_REGISTERED_HANDLERS = bl_handlers
msg = 'There are already BLHandlers registered; they must be unregistered before a new set can be registered.'
raise ValueError(msg)
def unregister_handlers() -> None:
"""Unregister this addon's registered Blender handlers."""
global _REGISTERED_HANDLERS # noqa: PLW0603
log.info('Unregistering BLHandlers') ## TODO: More information
if _REGISTERED_HANDLERS is not None:
_REGISTERED_HANDLERS.register()
_REGISTERED_HANDLERS = None
msg = 'There are no BLHandlers registered; therefore, there is nothing to register.'
raise ValueError(msg)
#################### ####################
# - Keymap Registration # - Keymap Registration
#################### ####################

View File

@ -193,6 +193,12 @@ ignore = [
"E501", # Let Formatter Worry about Line Length "E501", # Let Formatter Worry about Line Length
] ]
[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"D100", # It's okay to not have module-level docstrings in test modules.
"D104", # Same for packages.
]
#################### ####################
# - Tooling: Ruff Sublinters # - Tooling: Ruff Sublinters
#################### ####################