coll obj + fast ng access

This commit is contained in:
Nicolas Predella 2026-06-02 13:04:30 +03:00
parent d0c11a500c
commit ed047337ba
4 changed files with 78 additions and 11 deletions

View file

@ -32,6 +32,50 @@ class SLICENODE_InputObject(InputNode):
input_type = "Object" input_type = "Object"
bl_icon = "OBJECT_DATA" bl_icon = "OBJECT_DATA"
@register_class
class SLICENODE_CollectionObjects(SliceNode):
bl_label = "Collection Objects"
collection: bpy.props.PointerProperty(
name="Collection",
type=bpy.types.Collection,
update=notify_tree_changed
) # pyright: ignore[reportInvalidTypeForm]
def init(self, context):
self._update_outputs()
def update(self):
super().update()
self._update_outputs()
def draw_buttons(self, context, layout):
assert layout
layout.prop(self, "collection", text="")
def _update_outputs(self):
if not self.collection:
self.outputs.clear()
return
objs = [obj for obj in self.collection.objects if obj.type == 'MESH']
expected_names = [obj.name for obj in objs]
# Remove outputs that are no longer in the collection
to_remove = [o for o in self.outputs if o.name not in expected_names]
for o in to_remove:
self.outputs.remove(o)
# Add new outputs
for name in expected_names:
if name not in self.outputs:
self.add_output("SLICESOCK_ObjectRo", name, identifier=name)
def evaluate_output(self, socket_name: str, **kwargs) -> Any:
if self.collection and socket_name in bpy.data.objects:
return bpy.data.objects[socket_name]
return None
@register_class @register_class
class SLICENODE_InputSettings(InputNode): class SLICENODE_InputSettings(InputNode):
input_type = "Settings" input_type = "Settings"

View file

@ -44,8 +44,7 @@ class ReadonlySocket():
def draw(self, context: Any, layout: Any, node: Any, text: str | None): def draw(self, context: Any, layout: Any, node: Any, text: str | None):
return self._draw_as_ro(context, layout, node, text) # pyright: ignore[reportAttributeAccessIssue] return self._draw_as_ro(context, layout, node, text) # pyright: ignore[reportAttributeAccessIssue]
@register_class class SLICESOCK_ObjectCommon(InputNodeSocket):
class SLICESOCK_Object(InputNodeSocket):
bl_label = "Object" bl_label = "Object"
value: NfPointerProperty( value: NfPointerProperty(
@ -55,6 +54,10 @@ class SLICESOCK_Object(InputNodeSocket):
) # pyright: ignore[reportInvalidTypeForm] ) # pyright: ignore[reportInvalidTypeForm]
bl_color = (0.9, 0.7, 0.2, 1.0) bl_color = (0.9, 0.7, 0.2, 1.0)
@register_class
class SLICESOCK_ObjectRo(ReadonlySocket, SLICESOCK_ObjectCommon): ...
@register_class
class SLICESOCK_Object(SLICESOCK_ObjectCommon): ...
class SLICESOCK_Float_Common(InputNodeSocket): class SLICESOCK_Float_Common(InputNodeSocket):
bl_label = "Float" bl_label = "Float"

View file

@ -124,6 +124,11 @@ def depsgraph_update_handler(scene, depsgraph):
node.sync_sockets() node.sync_sockets()
except Exception: except Exception:
pass pass
elif node.bl_idname == "SLICENODE_CollectionObjects" and hasattr(node, "_update_outputs"):
try:
node._update_outputs()
except Exception:
pass
@register_class @register_class
@ -141,27 +146,33 @@ class SLICENODE_OT_GroupToggle(Operator):
bl_description = "Enter or exit the selected node group" bl_description = "Enter or exit the selected node group"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
node_name: bpy.props.StringProperty() # pyright: ignore[reportInvalidTypeForm]
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
assert context.space_data assert context.space_data
return (context.space_data.type == 'NODE_EDITOR' and return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == "SLICETREE_NodeTree") # pyright: ignore[reportAttributeAccessIssue] context.space_data.tree_type == "SLICETREE_NodeTree") # pyright: ignore[reportAttributeAccessIssue]
def execute(self, context) -> set[OperatorReturnItems]: def execute(self, context) -> set['OperatorReturnItems']:
space = context.space_data space = context.space_data
path = getattr(space, 'path', None) path = getattr(space, 'path', None)
if not path: if not path:
return {'CANCELLED'} return {'CANCELLED'}
tree = getattr(space, 'edit_tree', None)
# If we are inside a group, popping returns us to the parent target_node = None
# We want to exit if: 1. No node is selected, OR 2. We press Tab while editing inside a group if self.node_name and tree and self.node_name in tree.nodes:
active_node = context.active_node target_node = tree.nodes[self.node_name]
else:
target_node = context.active_node
# If we have an active group node, we try to enter it # If we have a target group node, we try to enter it
if active_node and active_node.bl_idname == "SLICENODE_Group": if target_node and target_node.bl_idname == "SLICENODE_Group":
group_tree = getattr(active_node, "slicer_tree", None) group_tree = getattr(target_node, "slicer_tree", None)
if group_tree: if group_tree:
path.append(group_tree, node=active_node) path.append(group_tree, node=target_node)
return {'FINISHED'} return {'FINISHED'}
# Otherwise, if we are nested, we pop out # Otherwise, if we are nested, we pop out
@ -171,6 +182,7 @@ class SLICENODE_OT_GroupToggle(Operator):
return {'CANCELLED'} return {'CANCELLED'}
@register_class @register_class
class SLICENODE_OT_GroupMake(Operator): class SLICENODE_OT_GroupMake(Operator):
bl_idname = "slicenode.group_make" bl_idname = "slicenode.group_make"
@ -451,6 +463,7 @@ class SLICENODE_MT_DataInput(Menu):
layout.operator("node.add_node", text="Vector").type = "SLICENODE_InputVector" layout.operator("node.add_node", text="Vector").type = "SLICENODE_InputVector"
layout.separator() layout.separator()
layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject" layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject"
layout.operator("node.add_node", text="Collection Objects").type = "SLICENODE_CollectionObjects"
layout.operator("node.add_node", text="Settings").type = "SLICENODE_InputSettings" layout.operator("node.add_node", text="Settings").type = "SLICENODE_InputSettings"
layout.operator("node.add_node", text="PrusaSlicer Config").type = "SLICENODE_PrusaslicerConf" layout.operator("node.add_node", text="PrusaSlicer Config").type = "SLICENODE_PrusaslicerConf"
layout.separator() layout.separator()
@ -542,6 +555,8 @@ class SLICENODE_MT_Group(Menu):
layout.operator("node.group_ungroup", text="Ungroup") layout.operator("node.group_ungroup", text="Ungroup")
layout.operator("slicenode.group_toggle", text="Edit Group") layout.operator("slicenode.group_toggle", text="Edit Group")
layout.separator() layout.separator()
layout.operator("node.add_node", text="Add Group Node").type = "SLICENODE_Group"
layout.separator()
layout.operator("node.add_node", text="Group Input").type = "NodeGroupInput" layout.operator("node.add_node", text="Group Input").type = "NodeGroupInput"
layout.operator("node.add_node", text="Group Output").type = "NodeGroupOutput" layout.operator("node.add_node", text="Group Output").type = "NodeGroupOutput"

View file

@ -179,6 +179,7 @@ def _get_current_group_node() -> bpy.types.Node | None:
@register_class @register_class
class SLICENODE_Group(bpy.types.NodeCustomGroup, AutoBlIdName): class SLICENODE_Group(bpy.types.NodeCustomGroup, AutoBlIdName):
bl_label = "Group" bl_label = "Group"
bl_icon = 'NODETREE'
def _tree_updated(self, context): def _tree_updated(self, context):
try: try:
@ -260,7 +261,11 @@ class SLICENODE_Group(bpy.types.NodeCustomGroup, AutoBlIdName):
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
assert layout assert layout
layout.prop(self, "slicer_tree", text="") row = layout.row(align=True)
row.prop(self, "slicer_tree", text="")
if self.slicer_tree:
op = row.operator("slicenode.group_toggle", text="", icon='NODETREE')
op.node_name = self.name
def draw_label(self): def draw_label(self):
if self.slicer_tree: if self.slicer_tree: