visualization

This commit is contained in:
Nicolas Predella 2026-05-20 18:44:29 +03:00
parent 716e619c28
commit f532fada23
3 changed files with 56 additions and 32 deletions

View file

@ -1315,26 +1315,24 @@ class SLICENODE_AirtableSubmit(ExecSliceNode_DatabaseSubmit):
return str(record_id) return str(record_id)
@register_class @register_class
class SLICENODE_PrusaslicerSlice(ExecSliceNode): class SLICENODE_PrusaslicerSlice(SliceNode):
bl_label = "PrusaSlicer Slice" bl_label = "PrusaSlicer Slice"
button_label = "Slice with PrusaSlicer"
prusaslicer_executable: NfStringProperty(name="PrusaSlicer Path", subtype='FILE_PATH', default='flatpak run com.prusa3d.PrusaSlicer') # pyright: ignore[reportInvalidTypeForm] prusaslicer_executable: NfStringProperty(name="PrusaSlicer Path", subtype='FILE_PATH', default='flatpak run com.prusa3d.PrusaSlicer') # pyright: ignore[reportInvalidTypeForm]
gcode_result: NfStringProperty(name="Gcode Result", default="") # pyright: ignore[reportInvalidTypeForm] gcode_result: NfStringProperty(name="Gcode Result", default="") # pyright: ignore[reportInvalidTypeForm]
def init(self, context): def init(self, context):
self._init_exec()
self.add_input("SLICESOCK_Mesh", "Mesh") self.add_input("SLICESOCK_Mesh", "Mesh")
self.add_input("SLICESOCK_PrusaslicerConf", "Config") self.add_input("SLICESOCK_PrusaslicerConf", "Config")
self.add_input("SLICESOCK_Overrides", "Overrides") self.add_input("SLICESOCK_Overrides", "Overrides")
self.add_output("SLICESOCK_GcodeRo") self.add_output("SLICESOCK_GcodeRo")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
super().draw_buttons(context, layout)
layout.prop(self, "prusaslicer_executable") layout.prop(self, "prusaslicer_executable")
def evaluate_output(self, socket_name: str, **kwargs) -> Any: def evaluate_output(self, socket_name: str, **kwargs) -> Any:
if socket_name == "Gcode": if socket_name == "Gcode":
self.evaluate()
return self.gcode_result return self.gcode_result
return super().evaluate_output(socket_name, **kwargs) return super().evaluate_output(socket_name, **kwargs)
@ -1603,6 +1601,7 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner):
layout.prop(self, "auto_refresh") layout.prop(self, "auto_refresh")
def evaluate(self, operator=None): def evaluate(self, operator=None):
from ...infra.blender.blender_slice_nodes_generic import node_execution_feedback
for i, job in enumerate(self.inputs): for i, job in enumerate(self.inputs):
# Resolve the input to get the connected node(s) # Resolve the input to get the connected node(s)
res = resolve_input(job) res = resolve_input(job)
@ -1620,6 +1619,7 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner):
operator.report({'INFO'}, f"Executing: {upstream_node.bl_label}") operator.report({'INFO'}, f"Executing: {upstream_node.bl_label}")
try: try:
with node_execution_feedback(upstream_node):
exec_res = upstream_node.evaluate(operator=operator) exec_res = upstream_node.evaluate(operator=operator)
if exec_res != 'ok' and exec_res is not None: if exec_res != 'ok' and exec_res is not None:
if operator: operator.report({'WARNING'}, f"Node {upstream_node.bl_label} returned {exec_res}") if operator: operator.report({'WARNING'}, f"Node {upstream_node.bl_label} returned {exec_res}")

View file

@ -5,6 +5,7 @@ from bpy.types import NodeTree, Operator, Menu
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
from ...infra.blender.mixins import update_collection from ...infra.blender.mixins import update_collection
from ...infra.blender.blender_slice_nodes_generic import node_execution_feedback, resolve_input
from ...registry import register_class from ...registry import register_class
if TYPE_CHECKING: if TYPE_CHECKING:
@ -84,6 +85,7 @@ def tree_update_handler(scene, depsgraph):
if should_refresh: if should_refresh:
_is_updating = True _is_updating = True
try: try:
with node_execution_feedback(node):
node.evaluate() node.evaluate()
except Exception as e: except Exception as e:
print(f"Auto-refresh failed: {e}") print(f"Auto-refresh failed: {e}")
@ -130,6 +132,7 @@ class SLICENODE_OT_Execute(Operator):
node = node_tree.nodes[self.node_name] node = node_tree.nodes[self.node_name]
try: try:
with node_execution_feedback(node):
res = node.evaluate(operator=self) res = node.evaluate(operator=self)
if res == 'ok': if res == 'ok':
self.report({"INFO"}, f"Execution Successful") self.report({"INFO"}, f"Execution Successful")

View file

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import typing import typing
import bpy
from contextlib import contextmanager
from typing import Any from typing import Any
from bpy.types import NodeSocket, Node from bpy.types import NodeSocket, Node
@ -41,6 +43,24 @@ class SliceNode(Node, AutoBlIdName):
## Evaluation helpers ## Evaluation helpers
@contextmanager
def node_execution_feedback(node: bpy.types.Node):
# Save original color
old_use_color = node.use_custom_color
old_color = node.color[:]
node.use_custom_color = True
node.color = (75/255, 73/255, 19/255) # Green
# Refresh UI
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
try:
yield
finally:
node.use_custom_color = old_use_color
node.color = old_color
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
def _follow_reroute(socket: NodeSocket) -> NodeSocket: def _follow_reroute(socket: NodeSocket) -> NodeSocket:
current: NodeSocket = socket current: NodeSocket = socket
seen: set[int] = set() seen: set[int] = set()
@ -106,6 +126,7 @@ def _resolve_output(
visited.add(key) visited.add(key)
try: try:
with node_execution_feedback(socket.node):
if not isinstance(socket.node, SliceNode): if not isinstance(socket.node, SliceNode):
return default return default