visualization
This commit is contained in:
parent
716e619c28
commit
f532fada23
3 changed files with 56 additions and 32 deletions
|
|
@ -1315,26 +1315,24 @@ class SLICENODE_AirtableSubmit(ExecSliceNode_DatabaseSubmit):
|
|||
return str(record_id)
|
||||
|
||||
@register_class
|
||||
class SLICENODE_PrusaslicerSlice(ExecSliceNode):
|
||||
class SLICENODE_PrusaslicerSlice(SliceNode):
|
||||
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]
|
||||
gcode_result: NfStringProperty(name="Gcode Result", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self._init_exec()
|
||||
self.add_input("SLICESOCK_Mesh", "Mesh")
|
||||
self.add_input("SLICESOCK_PrusaslicerConf", "Config")
|
||||
self.add_input("SLICESOCK_Overrides", "Overrides")
|
||||
self.add_output("SLICESOCK_GcodeRo")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
super().draw_buttons(context, layout)
|
||||
layout.prop(self, "prusaslicer_executable")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **kwargs) -> Any:
|
||||
if socket_name == "Gcode":
|
||||
self.evaluate()
|
||||
return self.gcode_result
|
||||
return super().evaluate_output(socket_name, **kwargs)
|
||||
|
||||
|
|
@ -1603,6 +1601,7 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner):
|
|||
layout.prop(self, "auto_refresh")
|
||||
|
||||
def evaluate(self, operator=None):
|
||||
from ...infra.blender.blender_slice_nodes_generic import node_execution_feedback
|
||||
for i, job in enumerate(self.inputs):
|
||||
# Resolve the input to get the connected node(s)
|
||||
res = resolve_input(job)
|
||||
|
|
@ -1620,9 +1619,10 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner):
|
|||
operator.report({'INFO'}, f"Executing: {upstream_node.bl_label}")
|
||||
|
||||
try:
|
||||
exec_res = upstream_node.evaluate(operator=operator)
|
||||
if exec_res != 'ok' and exec_res is not None:
|
||||
if operator: operator.report({'WARNING'}, f"Node {upstream_node.bl_label} returned {exec_res}")
|
||||
with node_execution_feedback(upstream_node):
|
||||
exec_res = upstream_node.evaluate(operator=operator)
|
||||
if exec_res != 'ok' and exec_res is not None:
|
||||
if operator: operator.report({'WARNING'}, f"Node {upstream_node.bl_label} returned {exec_res}")
|
||||
except Exception as e:
|
||||
if operator: operator.report({'ERROR'}, f"Failed executing {upstream_node.bl_label}: {str(e)}")
|
||||
return 'error'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from bpy.types import NodeTree, Operator, Menu
|
|||
from bpy.app.handlers import persistent
|
||||
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -84,7 +85,8 @@ def tree_update_handler(scene, depsgraph):
|
|||
if should_refresh:
|
||||
_is_updating = True
|
||||
try:
|
||||
node.evaluate()
|
||||
with node_execution_feedback(node):
|
||||
node.evaluate()
|
||||
except Exception as e:
|
||||
print(f"Auto-refresh failed: {e}")
|
||||
import traceback
|
||||
|
|
@ -130,9 +132,10 @@ class SLICENODE_OT_Execute(Operator):
|
|||
node = node_tree.nodes[self.node_name]
|
||||
|
||||
try:
|
||||
res = node.evaluate(operator=self)
|
||||
if res == 'ok':
|
||||
self.report({"INFO"}, f"Execution Successful")
|
||||
with node_execution_feedback(node):
|
||||
res = node.evaluate(operator=self)
|
||||
if res == 'ok':
|
||||
self.report({"INFO"}, f"Execution Successful")
|
||||
except Exception as e:
|
||||
self.report({"ERROR"}, f"Execution failed: {str(e)}")
|
||||
import traceback
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
import typing
|
||||
import bpy
|
||||
from contextlib import contextmanager
|
||||
|
||||
from typing import Any
|
||||
from bpy.types import NodeSocket, Node
|
||||
|
|
@ -41,6 +43,24 @@ class SliceNode(Node, AutoBlIdName):
|
|||
|
||||
## 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:
|
||||
current: NodeSocket = socket
|
||||
seen: set[int] = set()
|
||||
|
|
@ -106,27 +126,28 @@ def _resolve_output(
|
|||
|
||||
visited.add(key)
|
||||
try:
|
||||
if not isinstance(socket.node, SliceNode):
|
||||
return default
|
||||
with node_execution_feedback(socket.node):
|
||||
if not isinstance(socket.node, SliceNode):
|
||||
return default
|
||||
|
||||
if getattr(socket.node, "mute", False):
|
||||
# Attempt bypass: find a matching input socket by name or type
|
||||
input_to_bypass = socket.node.inputs.get(socket.name)
|
||||
if not input_to_bypass:
|
||||
for inp in socket.node.inputs:
|
||||
if inp.bl_idname == socket.bl_idname:
|
||||
input_to_bypass = inp
|
||||
break
|
||||
if getattr(socket.node, "mute", False):
|
||||
# Attempt bypass: find a matching input socket by name or type
|
||||
input_to_bypass = socket.node.inputs.get(socket.name)
|
||||
if not input_to_bypass:
|
||||
for inp in socket.node.inputs:
|
||||
if inp.bl_idname == socket.bl_idname:
|
||||
input_to_bypass = inp
|
||||
break
|
||||
|
||||
if input_to_bypass:
|
||||
value = resolve_input(typing.cast(Any, input_to_bypass), cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
return default
|
||||
if input_to_bypass:
|
||||
value = resolve_input(typing.cast(Any, input_to_bypass), cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
return default
|
||||
|
||||
assert socket.node.evaluate_output
|
||||
value = socket.node.evaluate_output(socket.name, cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
assert socket.node.evaluate_output
|
||||
value = socket.node.evaluate_output(socket.name, cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
finally:
|
||||
visited.remove(key)
|
||||
Loading…
Add table
Reference in a new issue