Add InputGroup to simplify large sets of inputs
This commit is contained in:
parent
e7bae54662
commit
aa11b398a0
4 changed files with 51 additions and 8 deletions
|
|
@ -4,6 +4,7 @@ import itertools
|
|||
import enum
|
||||
from .state import State
|
||||
from .types import *
|
||||
from .static.input_group import InputGroup
|
||||
from ..absolute_path import absolute_path
|
||||
|
||||
class OutputsList(dict):
|
||||
|
|
@ -13,6 +14,10 @@ class OutputsList(dict):
|
|||
|
||||
def build_node(node_type):
|
||||
def build(_primary_arg=None, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
if isinstance(v, InputGroup):
|
||||
kwargs = { **kwargs, **v.__dict__ }
|
||||
del kwargs[k]
|
||||
node = State.current_node_tree.nodes.new(node_type.__name__)
|
||||
if _primary_arg is not None:
|
||||
State.current_node_tree.links.new(_primary_arg._socket, node.inputs[0])
|
||||
|
|
@ -42,7 +47,7 @@ def build_node(node_type):
|
|||
value = kwargs[argname]
|
||||
if isinstance(value, enum.Enum):
|
||||
value = value.value
|
||||
if node_input.is_multi_input and hasattr(value, '__iter__') and len() > 0 and issubclass(type(next(iter(value))), Type):
|
||||
if node_input.is_multi_input and hasattr(value, '__iter__') and len(value) > 0 and issubclass(type(next(iter(value))), Type):
|
||||
for x in value:
|
||||
for node_input in all_with_name:
|
||||
State.current_node_tree.links.new(x._socket, node_input)
|
||||
|
|
@ -326,6 +331,14 @@ class Type:
|
|||
{newline.join(map(type_symbol, Type.__subclasses__()))}
|
||||
{newline.join(map(enum_namespace, enums.keys()))}
|
||||
{newline.join(symbols)}"""
|
||||
|
||||
static_path = absolute_path('api/static')
|
||||
for path in os.listdir(static_path):
|
||||
if os.path.splitext(path)[-1] != '.py':
|
||||
continue
|
||||
with open(os.path.join(static_path, path), 'r') as static_api:
|
||||
contents += f"\n\n# {path}\n{static_api.read()}"
|
||||
|
||||
fpyi.write(contents)
|
||||
fpy.write(contents)
|
||||
|
||||
|
|
|
|||
17
api/static/input_group.py
Normal file
17
api/static/input_group.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
class InputGroup:
|
||||
"""
|
||||
A group of inputs that will be expanded in the node tree.
|
||||
|
||||
All properties must be annotated:
|
||||
```python
|
||||
class MyInputGroup(InputGroup):
|
||||
my_float: Float
|
||||
my_bool: Bool
|
||||
my_string # Invalid
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for p in dir(self):
|
||||
if p in kwargs:
|
||||
setattr(self, p, kwargs[p])
|
||||
25
api/tree.py
25
api/tree.py
|
|
@ -7,6 +7,7 @@ except:
|
|||
from .state import State
|
||||
from .types import *
|
||||
from .node_mapper import *
|
||||
from .static.input_group import *
|
||||
|
||||
def _as_iterable(x):
|
||||
try:
|
||||
|
|
@ -39,12 +40,19 @@ def tree(name):
|
|||
|
||||
# Collect the inputs
|
||||
inputs = {}
|
||||
for param in signature.parameters.values():
|
||||
input_groups = {}
|
||||
def validate_param(param):
|
||||
if param.annotation == inspect.Parameter.empty:
|
||||
raise Exception(f"Tree input '{param.name}' has no type specified. Please annotate with a valid node input type.")
|
||||
if not issubclass(param.annotation, Type):
|
||||
raise Exception(f"Type of tree input '{param.name}' is not a valid 'Type' subclass.")
|
||||
inputs[param.name] = (param.annotation, param.default)
|
||||
for param in signature.parameters.values():
|
||||
if issubclass(param.annotation, InputGroup):
|
||||
for group_param, annotation in param.annotation.__annotations__.items():
|
||||
inputs[group_param] = (annotation, inspect.Parameter.empty, param.name)
|
||||
else:
|
||||
validate_param(param)
|
||||
inputs[param.name] = (param.annotation, param.default, None)
|
||||
|
||||
# Create the input sockets and collect input values.
|
||||
for i, node_input in enumerate(node_group.inputs):
|
||||
|
|
@ -52,7 +60,7 @@ def tree(name):
|
|||
for ni in node_group.inputs:
|
||||
node_group.inputs.remove(ni)
|
||||
break
|
||||
builder_inputs = []
|
||||
builder_inputs = {}
|
||||
for i, arg in enumerate(inputs.items()):
|
||||
input_name = arg[0].replace('_', ' ').title()
|
||||
if len(node_group.inputs) > i:
|
||||
|
|
@ -62,18 +70,23 @@ def tree(name):
|
|||
node_input = node_group.inputs.new(arg[1][0].socket_type, input_name)
|
||||
if arg[1][1] != inspect.Parameter.empty:
|
||||
node_input.default_value = arg[1][1]
|
||||
builder_inputs.append(arg[1][0](group_input_node.outputs[i]))
|
||||
if arg[1][2] is not None:
|
||||
if arg[1][2] not in builder_inputs:
|
||||
builder_inputs[arg[1][2]] = signature.parameters[arg[1][2]].annotation()
|
||||
setattr(builder_inputs[arg[1][2]], arg[0], arg[1][0](group_input_node.outputs[i]))
|
||||
else:
|
||||
builder_inputs[arg[0]] = arg[1][0](group_input_node.outputs[i])
|
||||
|
||||
# Run the builder function
|
||||
State.current_node_tree = node_group
|
||||
if inspect.isgeneratorfunction(builder):
|
||||
generated_outputs = [*builder(*builder_inputs)]
|
||||
generated_outputs = [*builder(**builder_inputs)]
|
||||
if all(map(lambda x: issubclass(type(x), Type) and x._socket.type == 'GEOMETRY', generated_outputs)):
|
||||
outputs = join_geometry(geometry=generated_outputs)
|
||||
else:
|
||||
outputs = generated_outputs
|
||||
else:
|
||||
outputs = builder(*builder_inputs)
|
||||
outputs = builder(**builder_inputs)
|
||||
|
||||
# Create the output sockets
|
||||
for i, result in enumerate(_as_iterable(outputs)):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import nodeitems_utils
|
|||
from .state import State
|
||||
|
||||
def map_case_name(i):
|
||||
return i.identifier.replace(' ', '_').upper()
|
||||
return ('_' if not i.identifier[0].isalpha() else '') + i.identifier.replace(' ', '_').upper()
|
||||
|
||||
# The base class all exposed socket types conform to.
|
||||
class Type:
|
||||
|
|
|
|||
Reference in a new issue