diff --git a/__init__.py b/__init__.py index 61ee78d..c5eeffc 100644 --- a/__init__.py +++ b/__init__.py @@ -16,9 +16,11 @@ bl_info = { def register(): registry.blender_register_classes() bpy.types.NODE_MT_add.append(tree.draw_add_menu) + bpy.app.handlers.depsgraph_update_post.append(tree.depsgraph_update_handler) print('Nodeslicer Loaded') def unregister(): + bpy.app.handlers.depsgraph_update_post.remove(tree.depsgraph_update_handler) bpy.types.NODE_MT_add.remove(tree.draw_add_menu) registry.blender_unregister_classes() print('Nodeslicer Unloaded') diff --git a/core/nodeslicer/nodes.py b/core/nodeslicer/nodes.py index 4592c56..ba28ad1 100644 --- a/core/nodeslicer/nodes.py +++ b/core/nodeslicer/nodes.py @@ -835,18 +835,28 @@ class SLICENODE_GcodeToFile(ExecSliceNode): @register_class class SLICENODE_GcodeToTextBlock(ExecSliceNode): bl_label = "Gcode to Text Block" - button_label = "Create Text Block" + button_label = "Update Text Block" + + text_block_name: bpy.props.StringProperty(name="Text Block Name", default="NodeSlicer_Gcode.gcode") # pyright: ignore[reportInvalidTypeForm] def init(self, context): self._init_exec() self.add_input("SLICESOCK_Gcode") + def draw_buttons(self, context, layout): + super().draw_buttons(context, layout) + layout.prop(self, "text_block_name") + def evaluate(self, operator=None): gcode: str = resolve_input(self.inputs.get("Gcode")) if not gcode: return 'ok' - text_block = bpy.data.texts.new(name="NodeSlicer_Gcode.gcode") + name = self.text_block_name + text_block = bpy.data.texts.get(name) + if text_block is None: + text_block = bpy.data.texts.new(name=name) + text_block.from_string(gcode) # Attempt to show in an open text editor @@ -857,9 +867,8 @@ class SLICENODE_GcodeToTextBlock(ExecSliceNode): for space in area.spaces: if space.type == 'TEXT_EDITOR': space.text = text_block - - return 'ok' + return 'ok' @register_class class SLICENODE_StartPrint(ExecSliceNode): bl_label = "Start Print" @@ -1033,9 +1042,15 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner): button_label = "Execute All" socket_type = "SLICESOCK_ExecutionLink" + auto_refresh: bpy.props.BoolProperty(name="Auto-refresh", default=False) # pyright: ignore[reportInvalidTypeForm] + def init(self, context): self._init_exec() + def draw_buttons(self, context, layout): + super().draw_buttons(context, layout) + layout.prop(self, "auto_refresh") + def evaluate(self, operator=None): for i, job in enumerate(self.inputs): # Resolve the input to get the connected node(s) diff --git a/core/nodeslicer/tree.py b/core/nodeslicer/tree.py index 2563188..0aca907 100644 --- a/core/nodeslicer/tree.py +++ b/core/nodeslicer/tree.py @@ -2,12 +2,72 @@ from __future__ import annotations from typing import TYPE_CHECKING import bpy from bpy.types import NodeTree, Operator, Menu +from bpy.app.handlers import persistent from ...registry import register_class if TYPE_CHECKING: from bpy.stub_internal.rna_enums import OperatorReturnItems +def get_referenced_objects(node, visited=None): + if visited is None: + visited = set() + if node in visited: + return set() + visited.add(node) + + objs = set() + # Check all input sockets for objects + for inp in node.inputs: + # If it's an object socket, it might have a value if not linked + if inp.bl_idname == "SLICESOCK_Object": + if not inp.is_linked and inp.value: + objs.add(inp.value) + + # Always follow links to find more objects upstream + if inp.is_linked: + for link in inp.links: + objs.update(get_referenced_objects(link.from_node, visited)) + + # Also check output sockets of the node itself, + # as some nodes might "hold" an object (like InputObject) + for outp in node.outputs: + if outp.bl_idname == "SLICESOCK_Object": + if outp.value: + objs.add(outp.value) + + return objs + +_is_updating = False + +@persistent +def depsgraph_update_handler(scene, depsgraph): + global _is_updating + if _is_updating: + return + + updated_objects = {update.id.name for update in depsgraph.updates if isinstance(update.id, bpy.types.Object)} + if not updated_objects: + return + + for tree in bpy.data.node_groups: + if tree.bl_idname != "SLICETREE_NodeTree": + continue + + for node in tree.nodes: + if node.bl_idname == "SLICENODE_Executor" and getattr(node, "auto_refresh", False): + referenced = get_referenced_objects(node) + if any(obj.name in updated_objects for obj in referenced): + _is_updating = True + try: + node.evaluate() + except Exception as e: + print(f"Auto-refresh failed: {e}") + import traceback + traceback.print_exc() + finally: + _is_updating = False + @register_class class SLICETREE_NodeTree(NodeTree): bl_idname = "SLICETREE_NodeTree"