Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,7 @@ __marimo__/
# Streamlit
.streamlit/secrets.toml

packages/tree_clipper_addon/src/tree_clipper_addon/_vendor/
packages/tree_clipper_addon/src/tree_clipper_addon/_vendor/

# Build artifacts
*.zip
2 changes: 1 addition & 1 deletion packages/tree_clipper/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tree-clipper"
version = "0.1.6"
version = "0.2.0"
dependencies = []
maintainers = [
{ name="Algebraic", email="tree.clipper@algebraic.games" },
Expand Down
2 changes: 1 addition & 1 deletion packages/tree_clipper/src/tree_clipper/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# these fields are in the top level JSON object
BLENDER_VERSION = "blender_version"
TREE_CLIPPER_VERSION = "tree_clipper_version"
CURRENT_TREE_CLIPPER_VERSION = "0.1.6" # tested to match pyproject.toml
CURRENT_TREE_CLIPPER_VERSION = "0.2.0" # tested to match pyproject.toml
MATERIAL_NAME = "name"
TREES = "node_trees"
EXTERNAL = "external"
Expand Down
17 changes: 14 additions & 3 deletions packages/tree_clipper/src/tree_clipper/import_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,13 @@ def _import_property_simple(
):
if self.debug_prints:
print(f"{from_root.to_str()}: defer setting enum default for now")
location = from_root.to_str()
self.set_socket_enum_defaults.append(
lambda: setattr(getter(), identifier, serialization)
(
lambda: setattr(getter(), identifier, serialization),
location,
serialization,
)
)
return

Expand Down Expand Up @@ -518,8 +523,14 @@ def getter() -> bpy.types.ShaderNodeTree:
)
self.current_tree = None

for func in self.set_socket_enum_defaults:
func()
for func, location, value in self.set_socket_enum_defaults:
try:
func()
except (TypeError, ValueError) as e:
self.report.warnings.append(
f"{location}: could not set enum default value {value!r} "
f"(valid options may be unavailable in this context): {e}"
)
self.set_socket_enum_defaults.clear()

self.report.last_getter = getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
SCENE_OT_Tree_Clipper_Import_Cache,
SCENE_OT_Tree_Clipper_Import_File_Prepare,
SCENE_OT_Tree_Clipper_Import_Clipboard_Prepare,
SCENE_OT_Tree_Clipper_Paste_As_Nodes,
SCENE_OT_Tree_Clipper_Import_Modal,
)

Expand All @@ -48,6 +49,7 @@
SCENE_OT_Tree_Clipper_Import_Cache,
SCENE_OT_Tree_Clipper_Import_File_Prepare,
SCENE_OT_Tree_Clipper_Import_Clipboard_Prepare,
SCENE_OT_Tree_Clipper_Paste_As_Nodes,
SCENE_PT_Tree_Clipper_Panel,
TreeClipperPreferences,
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ schema_version = "1.0.0"
# Example of manifest file for a Blender extension
# Change the values according to your extension
id = "tree_clipper"
version = "0.1.6" # should match tree_clipper (core logic)
version = "0.2.0" # should match tree_clipper (core logic)
name = "Tree Clipper"
tagline = "Export and import Blender node trees as JSON"
maintainer = "Algebraic <tree.clipper@algebraic.games>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,48 @@
_INTERMEDIATE_IMPORT_CACHE = None
TIMER = None

# Whether the next import should be unpacked directly into the active node tree
# (loose nodes on the canvas) instead of being wrapped in a single group node.
_UNPACK_INTO_ACTIVE_TREE = False


def _prepare_import_cache(
operator: bpy.types.Operator,
*,
string: str | None = None,
file_path: "Path | None" = None,
) -> bool:
"""Build the import cache from the clipboard or a file.

Reports a readable error (instead of raising a traceback) when the input is
empty or doesn't contain a valid Tree Clipper node tree. Returns True on
success, False if the calling operator should cancel.
"""
global _INTERMEDIATE_IMPORT_CACHE
source = "clipboard" if string is not None else "file"

if string is not None and not string.strip():
_INTERMEDIATE_IMPORT_CACHE = None
operator.report(
{"ERROR"},
"The clipboard is empty - copy a node tree (or a Tree Clipper string) first.",
)
return False

try:
_INTERMEDIATE_IMPORT_CACHE = ImportIntermediate(
string=string, file_path=file_path
)
except (ValueError, KeyError, RuntimeError, OSError) as exception:
_INTERMEDIATE_IMPORT_CACHE = None
operator.report(
{"ERROR"},
f"Could not read a Tree Clipper node tree from the {source}: {exception}",
)
return False

return True


class SCENE_OT_Tree_Clipper_Import_File_Prepare(bpy.types.Operator):
bl_idname = "scene.tree_clipper_import_file_prepare"
Expand All @@ -43,8 +85,10 @@ def invoke(
def execute(
self, context: bpy.types.Context
) -> set["rna_enums.OperatorReturnItems"]:
global _INTERMEDIATE_IMPORT_CACHE
_INTERMEDIATE_IMPORT_CACHE = ImportIntermediate(file_path=Path(self.input_file))
global _UNPACK_INTO_ACTIVE_TREE
if not _prepare_import_cache(self, file_path=Path(self.input_file)):
return {"CANCELLED"}
_UNPACK_INTO_ACTIVE_TREE = False

# seems impossible to use bl_idname here
bpy.ops.scene.tree_clipper_import_cache("INVOKE_DEFAULT") # ty: ignore[unresolved-attribute]
Expand All @@ -59,10 +103,38 @@ class SCENE_OT_Tree_Clipper_Import_Clipboard_Prepare(bpy.types.Operator):
def execute(
self, context: bpy.types.Context
) -> set["rna_enums.OperatorReturnItems"]:
global _INTERMEDIATE_IMPORT_CACHE
_INTERMEDIATE_IMPORT_CACHE = ImportIntermediate(
string=bpy.context.window_manager.clipboard # ty:ignore[possibly-missing-attribute]
)
global _UNPACK_INTO_ACTIVE_TREE
if not _prepare_import_cache(
self,
string=bpy.context.window_manager.clipboard, # ty:ignore[possibly-missing-attribute]
):
return {"CANCELLED"}
_UNPACK_INTO_ACTIVE_TREE = False

# seems impossible to use bl_idname here
bpy.ops.scene.tree_clipper_import_cache("INVOKE_DEFAULT") # ty: ignore[unresolved-attribute]
return {"FINISHED"}


class SCENE_OT_Tree_Clipper_Paste_As_Nodes(bpy.types.Operator):
bl_idname = "scene.tree_clipper_paste_as_nodes"
bl_label = "Paste as Nodes"
bl_description = (
"Import the node tree from the clipboard and unpack it directly into the "
"active node tree, instead of wrapping it in a single group node"
)
bl_options = {"REGISTER"}

def execute(
self, context: bpy.types.Context
) -> set["rna_enums.OperatorReturnItems"]:
global _UNPACK_INTO_ACTIVE_TREE
if not _prepare_import_cache(
self,
string=bpy.context.window_manager.clipboard, # ty:ignore[possibly-missing-attribute]
):
return {"CANCELLED"}
_UNPACK_INTO_ACTIVE_TREE = True

# seems impossible to use bl_idname here
bpy.ops.scene.tree_clipper_import_cache("INVOKE_DEFAULT") # ty: ignore[unresolved-attribute]
Expand Down Expand Up @@ -250,6 +322,10 @@ def modal(self, context: bpy.types.Context, event: bpy.types.Event):

_INTERMEDIATE_IMPORT_CACHE = None

post_import(context=context, event=event, report=report)
global _UNPACK_INTO_ACTIVE_TREE
unpack = _UNPACK_INTO_ACTIVE_TREE
_UNPACK_INTO_ACTIVE_TREE = False

post_import(context=context, event=event, report=report, unpack=unpack)

return {"FINISHED"}
2 changes: 2 additions & 0 deletions packages/tree_clipper_addon/src/tree_clipper_addon/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .operators_export import SCENE_OT_Tree_Clipper_Export_Prepare
from .operators_import import (
SCENE_OT_Tree_Clipper_Import_Clipboard_Prepare,
SCENE_OT_Tree_Clipper_Paste_As_Nodes,
SCENE_OT_Tree_Clipper_Import_File_Prepare,
)

Expand Down Expand Up @@ -40,4 +41,5 @@ def draw(self, context: bpy.types.Context) -> None:

import_col = self.layout.column() # ty:ignore[possibly-missing-attribute]
import_col.operator(SCENE_OT_Tree_Clipper_Import_Clipboard_Prepare.bl_idname)
import_col.operator(SCENE_OT_Tree_Clipper_Paste_As_Nodes.bl_idname)
import_col.operator(SCENE_OT_Tree_Clipper_Import_File_Prepare.bl_idname)
Loading
Loading