ref
This commit is contained in:
parent
2bfc1ef781
commit
e504e9b88e
9 changed files with 37 additions and 71 deletions
|
|
@ -17,7 +17,7 @@ class SLICENODE_PathsToGcode(SliceNode):
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_Paths")
|
self.add_input("SLICESOCK_Paths")
|
||||||
self.add_input("SLICESOCK_Intrinsics")
|
self.add_input("SLICESOCK_Intrinsics")
|
||||||
self.add_output("SLICESOCK_GcodeRo")
|
self.add_output("NodeSocketString")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
assert layout
|
assert layout
|
||||||
|
|
@ -53,7 +53,7 @@ class SLICENODE_GcodeBlock(SliceNode):
|
||||||
bl_label = "Gcode Block"
|
bl_label = "Gcode Block"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_output("SLICESOCK_Gcode", "Gcode")
|
self.add_output("NodeSocketString", "Gcode")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
return self.outputs['Gcode'].value
|
return self.outputs['Gcode'].value
|
||||||
|
|
@ -66,7 +66,7 @@ class SLICENODE_AssignMetadata(SliceNode):
|
||||||
self.add_input("NodeSocketString", "Name")
|
self.add_input("NodeSocketString", "Name")
|
||||||
self.add_input("NodeSocketString", "Filament Type")
|
self.add_input("NodeSocketString", "Filament Type")
|
||||||
self.add_input("NodeSocketString", "Printer Model")
|
self.add_input("NodeSocketString", "Printer Model")
|
||||||
self.add_output("SLICESOCK_GcodeRo", "Gcode")
|
self.add_output("NodeSocketString", "Gcode")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
name = self.resolve("Name")
|
name = self.resolve("Name")
|
||||||
|
|
@ -86,10 +86,10 @@ class SLICENODE_GcodeRegexReplace(SliceNode):
|
||||||
bl_label = "Gcode Regex Replace"
|
bl_label = "Gcode Regex Replace"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_Gcode", "Gcode")
|
self.add_input("NodeSocketString", "Gcode")
|
||||||
self.add_input("NodeSocketString", "Find")
|
self.add_input("NodeSocketString", "Find")
|
||||||
self.add_input("NodeSocketString", "Replace")
|
self.add_input("NodeSocketString", "Replace")
|
||||||
self.add_output("SLICESOCK_GcodeRo", "Gcode")
|
self.add_output("NodeSocketString", "Gcode")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
import re
|
import re
|
||||||
|
|
@ -154,7 +154,7 @@ class SLICENODE_GcodeHelper(SliceNode):
|
||||||
parameters: NfStringProperty(name="Params", default="") # pyright: ignore[reportInvalidTypeForm]
|
parameters: NfStringProperty(name="Params", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_output("SLICESOCK_GcodeRo")
|
self.add_output("NodeSocketString")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
assert layout
|
assert layout
|
||||||
|
|
@ -177,8 +177,8 @@ class SLICENODE_GcodeHelper(SliceNode):
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_JoinGcode(SliceNodeJoiner):
|
class SLICENODE_JoinGcode(SliceNodeJoiner):
|
||||||
bl_label = "Join Gcode"
|
bl_label = "Join Gcode"
|
||||||
socket_type = "SLICESOCK_Gcode"
|
socket_type = "NodeSocketString"
|
||||||
output_socket_type = "SLICESOCK_GcodeRo"
|
output_socket_type = "NodeSocketString"
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
from ....infra.blender.blender_slice_nodes_generic import resolve_input
|
from ....infra.blender.blender_slice_nodes_generic import resolve_input
|
||||||
|
|
@ -193,8 +193,8 @@ class SLICENODE_LoadGcode(SliceNode):
|
||||||
bl_label = "Load Gcode File"
|
bl_label = "Load Gcode File"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_FilePath", "Filepath")
|
self.add_input("NodeSocketString", "Filepath")
|
||||||
self.add_output("SLICESOCK_GcodeRo")
|
self.add_output("NodeSocketString")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
path = self.resolve("Filepath")
|
path = self.resolve("Filepath")
|
||||||
|
|
|
||||||
|
|
@ -590,10 +590,12 @@ class SLICENODE_TransformGeometry(SliceNode):
|
||||||
self.add_output("SLICESOCK_Geometry", "Geometry")
|
self.add_output("SLICESOCK_Geometry", "Geometry")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||||
if socket_name not in ("Geometry"):
|
if socket_name not in ("Geometry", "Mesh"):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
geom_data: Any = self.resolve("Geometry")
|
geom_data: Any = self.resolve("Geometry")
|
||||||
|
if geom_data is None:
|
||||||
|
geom_data = self.resolve("Mesh")
|
||||||
if geom_data is None:
|
if geom_data is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ class SLICENODE_GcodeToFile(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("SLICESOCK_DirPath", "Folder")
|
self.add_input("NodeSocketString", "Folder")
|
||||||
self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode")
|
self.add_multi_socket_group("NodeSocketString", name="Gcode")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
@ -73,7 +73,7 @@ class SLICENODE_GcodeToTextBlock(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("SLICESOCK_Gcode")
|
self.add_input("NodeSocketString")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
@ -109,7 +109,7 @@ class SLICENODE_StartPrint(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("SLICESOCK_Gcode")
|
self.add_input("NodeSocketString")
|
||||||
self.add_input("SLICESOCK_Printer")
|
self.add_input("SLICESOCK_Printer")
|
||||||
|
|
||||||
def evaluate(self, operator=None):
|
def evaluate(self, operator=None):
|
||||||
|
|
@ -178,7 +178,7 @@ class ExecSliceNode_DatabaseSubmit(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode")
|
self.add_multi_socket_group("NodeSocketString", name="Gcode")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class SLICENODE_GcodeToMesh(ExecSliceNode):
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("NodeSocketString", "Name")
|
self.add_input("NodeSocketString", "Name")
|
||||||
self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode")
|
self.add_multi_socket_group("NodeSocketString", name="Gcode")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class SLICENODE_PrinterList(ExecSliceNode):
|
||||||
printer_names.add(name)
|
printer_names.add(name)
|
||||||
sock = self.inputs.get(name)
|
sock = self.inputs.get(name)
|
||||||
if not sock:
|
if not sock:
|
||||||
self.add_input("SLICESOCK_GcodeRo", name)
|
self.add_input("NodeSocketString", name)
|
||||||
|
|
||||||
# Remove stale input sockets
|
# Remove stale input sockets
|
||||||
for sock in list(self.inputs):
|
for sock in list(self.inputs):
|
||||||
|
|
@ -162,7 +162,7 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_PrusaslicerConf", "Config")
|
self.add_input("SLICESOCK_PrusaslicerConf", "Config")
|
||||||
self.add_input("SLICESOCK_Settings", "Settings")
|
self.add_input("SLICESOCK_Settings", "Settings")
|
||||||
self.add_multi_socket_group("SLICESOCK_Mesh", "SLICESOCK_GcodeRo", name="Slicing Group", in_name="Mesh", out_name="Gcode", in_display_shape='LIST')
|
self.add_multi_socket_group("SLICESOCK_Mesh", "NodeSocketString", name="Slicing Group", in_name="Mesh", out_name="Gcode", in_display_shape='LIST')
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
assert layout
|
assert layout
|
||||||
|
|
@ -317,7 +317,7 @@ class SLICENODE_PrusaslicerPreview(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode")
|
self.add_multi_socket_group("NodeSocketString", name="Gcode")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
@ -468,7 +468,7 @@ class SLICENODE_Test(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("SLICESOCK_Gcode")
|
self.add_input("NodeSocketString")
|
||||||
|
|
||||||
def evaluate(self, operator=None):
|
def evaluate(self, operator=None):
|
||||||
_ = self.resolve("Gcode")
|
_ = self.resolve("Gcode")
|
||||||
|
|
|
||||||
|
|
@ -44,18 +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]
|
||||||
|
|
||||||
# Native Blender socket type aliases
|
|
||||||
SLICESOCK_Object = "NodeSocketObject"
|
|
||||||
SLICESOCK_ObjectRo = "NodeSocketObject"
|
|
||||||
SLICESOCK_Float = "NodeSocketFloat"
|
|
||||||
SLICESOCK_FloatRo = "NodeSocketFloat"
|
|
||||||
SLICESOCK_Int = "NodeSocketInt"
|
|
||||||
SLICESOCK_IntRo = "NodeSocketInt"
|
|
||||||
SLICESOCK_String = "NodeSocketString"
|
|
||||||
SLICESOCK_VectorRotation = "NodeSocketRotation"
|
|
||||||
SLICESOCK_VectorRotationRo = "NodeSocketRotation"
|
|
||||||
SLICESOCK_Vector = "NodeSocketVector"
|
|
||||||
SLICESOCK_VectorRo = "NodeSocketVector"
|
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICESOCK_Geometry(LabelNodeSocket):
|
class SLICESOCK_Geometry(LabelNodeSocket):
|
||||||
|
|
@ -86,20 +75,6 @@ class SLICESOCK_Geometry(LabelNodeSocket):
|
||||||
else:
|
else:
|
||||||
return _get_upstream_color(self)
|
return _get_upstream_color(self)
|
||||||
|
|
||||||
@register_class
|
|
||||||
class SLICESOCK_FilePath(InputNodeSocket):
|
|
||||||
bl_label = "Filepath"
|
|
||||||
|
|
||||||
value: NfStringProperty(name="Filepath", default="", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm]
|
|
||||||
bl_color = (0.4, 0.7, 1.0, 1.0)
|
|
||||||
|
|
||||||
@register_class
|
|
||||||
class SLICESOCK_DirPath(InputNodeSocket):
|
|
||||||
bl_label = "Filepath"
|
|
||||||
|
|
||||||
value: NfStringProperty(name="Folder", default="", subtype='DIR_PATH') # pyright: ignore[reportInvalidTypeForm]
|
|
||||||
bl_color = (0.4, 0.7, 1.0, 1.0)
|
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICESOCK_Mesh(LabelNodeSocket):
|
class SLICESOCK_Mesh(LabelNodeSocket):
|
||||||
bl_label = "Mesh"
|
bl_label = "Mesh"
|
||||||
|
|
@ -112,18 +87,6 @@ class SLICESOCK_Paths(LabelNodeSocket):
|
||||||
default = SlicePaths()
|
default = SlicePaths()
|
||||||
bl_color = (214/255, 0, 120/255, 1.0)
|
bl_color = (214/255, 0, 120/255, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SLICESOCK_Gcode_Common(InputNodeSocket):
|
|
||||||
bl_label = "Gcode"
|
|
||||||
value: NfStringProperty(name="Gcode", default="") # pyright: ignore[reportInvalidTypeForm]
|
|
||||||
bl_color = (1., 1., 1., 1.)
|
|
||||||
|
|
||||||
@register_class
|
|
||||||
class SLICESOCK_Gcode(SLICESOCK_Gcode_Common): ...
|
|
||||||
@register_class
|
|
||||||
class SLICESOCK_GcodeRo(ReadonlySocket, SLICESOCK_Gcode_Common): ...
|
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICESOCK_Intrinsics(LabelNodeSocket):
|
class SLICESOCK_Intrinsics(LabelNodeSocket):
|
||||||
bl_label = "Intrinsics"
|
bl_label = "Intrinsics"
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ def object_init_handler(scene, depsgraph):
|
||||||
|
|
||||||
update_collection(pg.modifiers)
|
update_collection(pg.modifiers)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def depsgraph_update_handler(scene, depsgraph):
|
def depsgraph_update_handler(scene, depsgraph):
|
||||||
global _is_updating, _tree_changed
|
global _is_updating, _tree_changed
|
||||||
|
|
@ -134,7 +136,7 @@ def depsgraph_update_handler(scene, depsgraph):
|
||||||
tree_update_handler(scene, depsgraph)
|
tree_update_handler(scene, depsgraph)
|
||||||
object_init_handler(scene, depsgraph)
|
object_init_handler(scene, depsgraph)
|
||||||
|
|
||||||
# Sync SLICENODE_Group sockets if interface changed
|
# Sync SLICENODE_Group sockets if interface changed and migrate legacy sockets
|
||||||
for tree in bpy.data.node_groups:
|
for tree in bpy.data.node_groups:
|
||||||
if tree.bl_idname == "SLICETREE_NodeTree":
|
if tree.bl_idname == "SLICETREE_NodeTree":
|
||||||
for node in tree.nodes:
|
for node in tree.nodes:
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,12 @@
|
||||||
| `NodeSocketObject` | `bpy.types.Object` | Blender RNA Object | Reference to a Blender Object |
|
| `NodeSocketObject` | `bpy.types.Object` | Blender RNA Object | Reference to a Blender Object |
|
||||||
| `NodeSocketFloat` | `float` | Python float | Numeric value |
|
| `NodeSocketFloat` | `float` | Python float | Numeric value |
|
||||||
| `NodeSocketInt` | `int` | Python int | Integer value |
|
| `NodeSocketInt` | `int` | Python int | Integer value |
|
||||||
| `NodeSocketString` | `str` | Python string | Text value |
|
| `NodeSocketString` | `str` | Python string | Text, G-code, file/folder path value |
|
||||||
| `NodeSocketVector` | `tuple` | Python tuple (3) | XYZ vector value |
|
| `NodeSocketVector` | `tuple` | Python tuple (3) | XYZ vector value |
|
||||||
| `NodeSocketRotation` | `tuple` | Python tuple (3) | Euler rotation value |
|
| `NodeSocketRotation` | `tuple` | Python tuple (3) | Euler rotation value |
|
||||||
| `NodeSocketGeometry` | `Geometry` | Geometry Data | Generic geometry reference |
|
| `NodeSocketGeometry` | `Geometry` | Geometry Data | Generic geometry reference |
|
||||||
| `SLICESOCK_Mesh` | `SlicingObject` | NumPy NDArray (Triangles) | Internal mesh data (Transformed/Scaled) |
|
| `SLICESOCK_Mesh` | `SlicingObject` | NumPy NDArray (Triangles) | Internal mesh data (Transformed/Scaled) |
|
||||||
| `SLICESOCK_Paths` | `SlicePaths` (list) | List of paths (N, 3) + groups | Generic toolpaths or slice boundaries |
|
| `SLICESOCK_Paths` | `SlicePaths` (list) | List of paths (N, 3) + groups | Generic toolpaths or slice boundaries |
|
||||||
| `SLICESOCK_Gcode` | `str` | Multi-line string | Generated G-code text |
|
|
||||||
| `SLICESOCK_Settings` | `dict[str, str]` | Python dictionary | Mapping of configuration fields to values |
|
| `SLICESOCK_Settings` | `dict[str, str]` | Python dictionary | Mapping of configuration fields to values |
|
||||||
| `SLICESOCK_SettingsRo` | `dict[str, str]` | Python dictionary | Read-only merged configuration overrides |
|
| `SLICESOCK_SettingsRo` | `dict[str, str]` | Python dictionary | Read-only merged configuration overrides |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -436,26 +436,26 @@ class TestTransform(unittest.TestCase):
|
||||||
node.evaluate()
|
node.evaluate()
|
||||||
node._process_gcode.assert_called_once_with("GCODE1", "Part", None)
|
node._process_gcode.assert_called_once_with("GCODE1", "Part", None)
|
||||||
|
|
||||||
# 2. Three parts - padded to 2 digits (01, 02, 03)
|
# 2. Three parts - padded to 2 digits (00, 01, 02)
|
||||||
node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2", "GC3"])
|
node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2", "GC3"])
|
||||||
node.resolve = MagicMock(return_value="Part")
|
node.resolve = MagicMock(return_value="Part")
|
||||||
node._process_gcode = MagicMock()
|
node._process_gcode = MagicMock()
|
||||||
|
|
||||||
node.evaluate()
|
node.evaluate()
|
||||||
self.assertEqual(node._process_gcode.call_count, 3)
|
self.assertEqual(node._process_gcode.call_count, 3)
|
||||||
node._process_gcode.assert_any_call("GC1", "Part01", None)
|
node._process_gcode.assert_any_call("GC1", "Part00", None)
|
||||||
node._process_gcode.assert_any_call("GC2", "Part02", None)
|
node._process_gcode.assert_any_call("GC2", "Part01", None)
|
||||||
node._process_gcode.assert_any_call("GC3", "Part03", None)
|
node._process_gcode.assert_any_call("GC3", "Part02", None)
|
||||||
|
|
||||||
# 3. Fifteen parts - padded to 2 digits (01..15)
|
# 3. Fifteen parts - padded to 2 digits (00..14)
|
||||||
node.resolve_multi_input = MagicMock(return_value=[f"GC{i}" for i in range(1, 16)])
|
node.resolve_multi_input = MagicMock(return_value=[f"GC{i}" for i in range(1, 16)])
|
||||||
node.resolve = MagicMock(return_value="Part")
|
node.resolve = MagicMock(return_value="Part")
|
||||||
node._process_gcode = MagicMock()
|
node._process_gcode = MagicMock()
|
||||||
|
|
||||||
node.evaluate()
|
node.evaluate()
|
||||||
self.assertEqual(node._process_gcode.call_count, 15)
|
self.assertEqual(node._process_gcode.call_count, 15)
|
||||||
node._process_gcode.assert_any_call("GC1", "Part01", None)
|
node._process_gcode.assert_any_call("GC1", "Part00", None)
|
||||||
node._process_gcode.assert_any_call("GC15", "Part15", None)
|
node._process_gcode.assert_any_call("GC15", "Part14", None)
|
||||||
|
|
||||||
# 4. Default base name when empty name provided
|
# 4. Default base name when empty name provided
|
||||||
node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2"])
|
node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2"])
|
||||||
|
|
@ -463,8 +463,8 @@ class TestTransform(unittest.TestCase):
|
||||||
node._process_gcode = MagicMock()
|
node._process_gcode = MagicMock()
|
||||||
|
|
||||||
node.evaluate()
|
node.evaluate()
|
||||||
node._process_gcode.assert_any_call("GC1", "GcodePreview01", None)
|
node._process_gcode.assert_any_call("GC1", "GcodePreview00", None)
|
||||||
node._process_gcode.assert_any_call("GC2", "GcodePreview02", None)
|
node._process_gcode.assert_any_call("GC2", "GcodePreview01", None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue