2025-07-01
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class PrintProperty(bpy.types.PropertyGroup):
|
||||
|
||||
text: bpy.props.StringProperty()
|
||||
|
||||
|
||||
|
||||
class SN_OT_ClearPrints(bpy.types.Operator):
|
||||
bl_idname = "sn.clear_prints"
|
||||
bl_label = "Clear Prints"
|
||||
bl_description = "Clear this print nodes messages"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
node_tree: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
|
||||
node: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
bpy.data.node_groups[self.node_tree].nodes[self.node].messages.clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
|
||||
class SN_PrintNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_PrintNode"
|
||||
bl_label = "Print"
|
||||
bl_width_default = 200
|
||||
node_color = "PROGRAM"
|
||||
|
||||
messages: bpy.props.CollectionProperty(type=PrintProperty)
|
||||
|
||||
print_on_node: bpy.props.BoolProperty(default=True,
|
||||
name="Print On Node",
|
||||
description="Show print results on this node",
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
limit_prints: bpy.props.IntProperty(default=0, min=0,
|
||||
name="Amount of print messages that will be added to this node before a previous one is removed (0 is unlimited)",
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_input()
|
||||
self.add_string_input()
|
||||
self.add_dynamic_string_input()
|
||||
self.add_execute_output()
|
||||
|
||||
def evaluate(self, context):
|
||||
self.messages.clear()
|
||||
values = [inp.python_value for inp in self.inputs[1:-1]]
|
||||
self.code_imperative = f"""
|
||||
def sn_print(on_node, node, limit, *args):
|
||||
print(*args)
|
||||
if on_node:
|
||||
try:
|
||||
msg = node.messages.add()
|
||||
msg.text = str(args)[1:-1]
|
||||
if limit and len(node.messages) > limit:
|
||||
node.messages.remove(0)
|
||||
for area in bpy.context.screen.areas: area.tag_redraw()
|
||||
except:
|
||||
print("Can't add print outputs to the node when the print is run in an interface!")
|
||||
"""
|
||||
self.code = f"""
|
||||
sn_print({self.print_on_node}, bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}'], {self.limit_prints}, {", ".join(values)})
|
||||
{self.indent(self.outputs[0].python_value, 5)}
|
||||
"""
|
||||
|
||||
def evaluate_export(self, context):
|
||||
self.messages.clear()
|
||||
values = [inp.python_value for inp in self.inputs[1:-1]]
|
||||
self.code = f"""
|
||||
print({", ".join(values)})
|
||||
{self.indent(self.outputs[0].python_value, 5)}
|
||||
"""
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "print_on_node", text="Print On Node")
|
||||
if self.print_on_node:
|
||||
layout.prop(self, "limit_prints", text="Limit")
|
||||
if self.print_on_node:
|
||||
if not self.messages:
|
||||
box = layout.box()
|
||||
box.label(text="Nothing printed!")
|
||||
else:
|
||||
row = layout.row()
|
||||
row.label(text="Messages:")
|
||||
op = row.operator("sn.clear_prints", text="", icon="TRASH", emboss=False)
|
||||
op.node_tree = self.node_tree.name
|
||||
op.node = self.name
|
||||
col = layout.column(align=True)
|
||||
col.scale_y = 0.9
|
||||
for msg in self.messages:
|
||||
box = col.box()
|
||||
box.label(text=msg.text)
|
||||
@@ -0,0 +1,23 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_SleepNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_SleepNode"
|
||||
bl_label = "Sleep"
|
||||
bl_width_default = 200
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_input()
|
||||
self.add_float_input("Seconds")
|
||||
self.add_execute_output()
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = "import time"
|
||||
self.code = f"""
|
||||
time.sleep({self.inputs[1].python_value})
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
@@ -0,0 +1,103 @@
|
||||
import bpy
|
||||
from random import uniform
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_TimestampNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_TimestampNode"
|
||||
bl_label = "Timestamp"
|
||||
bl_width_default = 200
|
||||
|
||||
def update_connected_portals(self, context=None):
|
||||
if self.timestamp_type == "START":
|
||||
for ntree in bpy.data.node_groups:
|
||||
if ntree.bl_idname == "ScriptingNodesTree":
|
||||
for node in ntree.node_collection(self.bl_idname).nodes:
|
||||
if node.timestamp_type == "END" and node.var_name == self.var_name:
|
||||
node.var_name = self.var_name
|
||||
|
||||
timestamp_type: bpy.props.EnumProperty(name="Type",
|
||||
description="Start the timestamp recording or print the total time since the start",
|
||||
items=[("START", "Start", "Start", "MOD_TIME", 0),
|
||||
("END", "End", "End", "TIME", 1)],
|
||||
update=update_connected_portals)
|
||||
|
||||
timestamp: bpy.props.FloatProperty(name="Timestamp")
|
||||
|
||||
def update_var_name(self, context):
|
||||
self.label = self.var_name
|
||||
self._evaluate(context)
|
||||
|
||||
def get_var_name(self):
|
||||
return self.get("var_name", "")
|
||||
|
||||
def set_var_name(self, value):
|
||||
if self.timestamp_type == "INPUT":
|
||||
for ntree in bpy.data.node_groups:
|
||||
if ntree.bl_idname == "ScriptingNodesTree":
|
||||
for node in ntree.node_collection(self.bl_idname).nodes:
|
||||
if node.timestamp_type == "OUTPUT" and node.var_name == self.var_name:
|
||||
node["var_name"] = value
|
||||
self["var_name"] = value
|
||||
|
||||
var_name: bpy.props.StringProperty(name="Name",
|
||||
description="The identifier that links this timestamp to other timestamps",
|
||||
get=get_var_name, set=set_var_name,
|
||||
update=update_var_name)
|
||||
|
||||
def update_custom_color(self, context):
|
||||
# update own color
|
||||
self.color = self.custom_color
|
||||
# update connected color
|
||||
if self.timestamp_type == "START":
|
||||
for ntree in bpy.data.node_groups:
|
||||
if ntree.bl_idname == "ScriptingNodesTree":
|
||||
for node in ntree.node_collection(self.bl_idname).nodes:
|
||||
if node.timestamp_type == "END" and node.var_name == self.var_name:
|
||||
node.custom_color = self.custom_color
|
||||
|
||||
custom_color: bpy.props.FloatVectorProperty(name="Color",
|
||||
size=3, min=0, max=1, subtype="COLOR",
|
||||
description="The color of this node",
|
||||
update=update_custom_color)
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_input()
|
||||
self.add_execute_output()
|
||||
self.var_name = self.uuid
|
||||
self.custom_color = (uniform(0,1), uniform(0,1), uniform(0,1))
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = "import time"
|
||||
if self.timestamp_type == "START":
|
||||
self.code = f"""
|
||||
t_{self.var_name} = time.time()
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
elif self.timestamp_type == "END":
|
||||
self.code = f"""
|
||||
if "t_{self.var_name}" in locals(): print(f"Time elapsed for {self.var_name}: {{(time.time() - t_{self.var_name}) * 1000}}ms")
|
||||
try: bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}'].timestamp = time.time() - t_{self.var_name}
|
||||
except: print("Failed to set timestamp on node")
|
||||
for a in bpy.context.screen.areas: a.tag_redraw()
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
|
||||
def evaluate_export(self, context):
|
||||
self.code = f"""
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "timestamp_type", expand=True)
|
||||
if self.timestamp_type == "START":
|
||||
row = layout.row(align=True)
|
||||
split = row.split(factor=0.6, align=True)
|
||||
split.prop(self, "var_name", text="")
|
||||
sub_split = split.split(factor=0.5, align=True)
|
||||
sub_split.prop(self, "custom_color", text="")
|
||||
sub_split.operator("sn.reset_portal", text="", icon="LOOP_BACK").node = self.name
|
||||
else:
|
||||
layout.label(text=f"Time elapsed: {self.timestamp*1000}ms")
|
||||
@@ -0,0 +1,55 @@
|
||||
import operator
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
class SN_OT_TriggerTriggerNode(bpy.types.Operator):
|
||||
bl_idname = "sn.trigger_trigger_node"
|
||||
bl_label = "Trigger Node"
|
||||
bl_description = "Trigger this node"
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
|
||||
uid: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
if self.uid in context.scene.sn.function_store:
|
||||
context.scene.sn.function_store[self.uid]()
|
||||
else:
|
||||
self.report({"WARNING"}, message="Couldn't find trigger function!")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
|
||||
class SN_TriggerNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_TriggerNode"
|
||||
bl_label = "Trigger"
|
||||
is_trigger = True
|
||||
bl_width_default = 200
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_output()
|
||||
|
||||
|
||||
def draw_node(self,context,layout):
|
||||
row = layout.row()
|
||||
row.scale_y = 1.2
|
||||
op = row.operator("sn.trigger_trigger_node", text="Run Trigger", icon="PLAY")
|
||||
op.uid = self.static_uid
|
||||
|
||||
|
||||
@property
|
||||
def handler_name(self):
|
||||
return f"trigger_handler_{self.static_uid}"
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code = f"""
|
||||
def {self.handler_name}():
|
||||
{self.indent(self.outputs[0].python_value, 7) if self.outputs[0].python_value.strip() else 'pass'}
|
||||
"""
|
||||
self.code_register = f"bpy.context.scene.sn.function_store['{self.static_uid}'] = {self.handler_name}"
|
||||
|
||||
def evaluate_export(self, context):
|
||||
self.code = ""
|
||||
self.code_register = ""
|
||||
Reference in New Issue
Block a user