Repository navigation
Expand file tree
/
Copy pathinterface.py
More file actions
142 lines (124 loc) · 5.23 KB
/
Copy pathinterface.py
File metadata and controls
142 lines (124 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""Group interface socket defaults and default-value persistence helpers."""
from .storage import INPUT_DEFAULTS_PROP
from .errors import CompileError
def _json_safe_default(value):
"""Function `_json_safe_default` used by the NodeForge addon."""
if value is None:
return None
if isinstance(value, bool):
return value
if isinstance(value, int):
return int(value)
if isinstance(value, float):
return float(value)
if isinstance(value, (tuple, list)):
return [float(v) for v in value]
return value
def _set_socket_default(socket, value):
"""Set a Blender socket/interface default robustly across socket classes."""
if socket is None or value is None:
return False
try:
if isinstance(value, (tuple, list)):
# Vector sockets usually expose a mutable default_value sequence.
try:
socket.default_value = (float(value[0]), float(value[1]), float(value[2]))
except Exception:
socket.default_value[0] = float(value[0])
socket.default_value[1] = float(value[1])
socket.default_value[2] = float(value[2])
elif isinstance(value, str):
socket.default_value = value
elif isinstance(value, bool):
socket.default_value = bool(value)
elif isinstance(value, int):
socket.default_value = int(value)
else:
socket.default_value = float(value)
return True
except Exception:
return False
def _set_interface_socket_default(group, name, in_out, value):
"""Function `_set_interface_socket_default` used by the NodeForge addon."""
if value is None:
return False
ok = False
try:
for item in group.interface.items_tree:
if getattr(item, "item_type", None) == 'SOCKET' and getattr(item, "name", None) == name and getattr(item, "in_out", None) == in_out:
ok = _set_socket_default(item, value) or ok
except Exception:
pass
return ok
def _record_group_input_default(group, name, typ, default):
"""Function `_record_group_input_default` used by the NodeForge addon."""
if default is None:
return
try:
data = dict(group.get(INPUT_DEFAULTS_PROP, {}))
except Exception:
data = {}
data[name] = {"type": typ, "default": _json_safe_default(default)}
try:
group[INPUT_DEFAULTS_PROP] = data
except Exception:
pass
def _idprop_to_plain(value):
"""Convert Blender IDProperty groups/arrays into ordinary Python values."""
if value is None:
return None
if isinstance(value, (str, int, float, bool)):
return value
if isinstance(value, (tuple, list)):
return [_idprop_to_plain(v) for v in value]
try:
# IDPropertyArray and bpy_prop_array are iterable but not dict-like.
if not hasattr(value, "items"):
return [_idprop_to_plain(v) for v in value]
except Exception:
pass
try:
return {str(k): _idprop_to_plain(v) for k, v in value.items()}
except Exception:
return value
def _get_group_input_defaults(group):
"""Function `_get_group_input_defaults` used by the NodeForge addon."""
if group is None:
return {}
try:
data = group.get(INPUT_DEFAULTS_PROP, {})
except Exception:
data = {}
data = _idprop_to_plain(data)
return data if isinstance(data, dict) else {}
def _is_root_interface_item(item):
"""Return whether a Blender interface item is attached to the implicit root panel."""
parent = getattr(item, "parent", None)
if parent is None:
return True
# Blender 5.x exposes the root as an unnamed synthetic panel rather than None.
return getattr(parent, "name", "") == ""
def _create_interface_panel(group, sockets, name, *, collapsed=False):
"""Create one root native interface panel and move validated input sockets into it."""
sockets = list(sockets)
if not sockets:
raise CompileError("panel() requires at least one input")
if not isinstance(name, str) or not name:
raise CompileError("panel() name must be a non-empty string")
for item in getattr(group.interface, "items_tree", []):
if (
getattr(item, "item_type", None) == "PANEL"
and _is_root_interface_item(item)
and getattr(item, "name", None) == name
):
raise CompileError(f'panel() duplicate panel name: {name}')
for socket in sockets:
if getattr(socket, "item_type", None) != "SOCKET" or getattr(socket, "in_out", None) != "INPUT":
raise CompileError("panel() can contain only group input sockets")
if not _is_root_interface_item(socket):
raise CompileError(f'panel() input {getattr(socket, "name", "<unnamed>")} already belongs to a panel')
panel = group.interface.new_panel(name=name, description="", default_closed=bool(collapsed))
for index, socket in enumerate(sockets):
group.interface.move_to_parent(socket, panel, index)
return panel
__all__ = ['_json_safe_default', '_set_socket_default', '_set_interface_socket_default', '_record_group_input_default', '_idprop_to_plain', '_get_group_input_defaults', '_create_interface_panel']