Open ly29 opened 8 years ago
The first idea is to enable setting arguments via decorater like follows.
@node_func(label="Input Int")
def int_in() -> [("Value", ValueInteger)]:
pass
Instead of
def int_in() -> [("Value", ValueInteger)]:
pass
int_in.label = "Input Int Value"
In essence doing something similar to:
def annotate(func, **values):
for key, value in values.items():
setattr(func, key, value)
def add(a,b):
return a+b
annotate(add, label="Add")
add.label
> "Add"
After eating lunch and messing with decorators for a while I came up with this.
def node_func(**values):
def real_node_func(func):
def annotate(func):
for key, value in values.items():
setattr(func, key, value)
return func
return annotate(func)
return real_node_func
>>> @node_func(key="Beta", label="Blue")
... def test(a: int =1, b: float = 1.0) -> float:
... return a+b
>>> test.label
'Blue'
>>> test.__annotations__
{'b': <class 'float'>, 'return': <class 'float'>, 'a': <class 'int'>}
Should @node_func
be required or optional? Required, better than the SvRxFunc = [add, sub]
syntax, more direct.
Current version as below.
def node_func(**values):
def real_node_func(func):
def annotate(func):
for key, value in values.items():
setattr(func, key, value)
return func
annotate(func)
register_node(func)
return func
return real_node_fun
Note that this requires ()
like this even when not setting any arguments, sure it could be solved.
@node_func()
def add(x: Number = 0.0, y: Number = 1.0) -> [("res", Number)]:
return x + y
It is solved, just required some thinking...
def node_func(*args, **values):
def real_node_func(func):
def annotate(func):
for key, value in values.items():
setattr(func, key, value)
return func
annotate(func)
register_node(func)
return func
if args and callable(args[0]):
return real_node_func(args[0])
else:
return real_node_fun
Usage like
# where you can pass set optional parameters on the function object.
@node_func(label="Input Int")
def int_in() -> [("Value", ValueInteger)]:
pass
# or just register it and leave everything as default.
@node_func
def add(x: Number = 0.0, y: Number = 1.0) -> [("res", Number)]:
return x + y
Maybe a more sane name would be register_node
@zeffii Comments?
yes let me read.
quick first comment, the key=beta, OOh i like that.. does that imply the menu/indexing of nodes and their locations will be automated into the node definition? :)
I meant to imply that such a behavior. Where the default will be depending on the module the node functions is placed in. But overridable with category="Alpaha"
for example.
A @register_node
that takes care of the SvRxFunc = [add, sub]
, looks altogether more cohesive. I'm still trying to mentally compile the code. may take a while.
Remember:
@decorate
def func(a,b):
return something
# is the same as
def func(a, b):
return something
func = decorate(func)
# and
@decorate(c)
def func(a, b)
return something
func = decorate(c)(func)
what about docstrings for each node, how do you feel about that? could help simplify documentation by being able to view node docs in the text editor.. I have some ideas about this, maybe worth its own thread) -- I feel the current way is OK, but slightly work-intense.
@ly29 I think when it comes time to start writing a larger set of nodes that we'll get a better feel for the good and (possibly?.. i'm not pessimistic) weaknesses of being this abstract. I welcome it, and look forward to writing a few new nodes this way.
I think it's good to take charge for 1 person here, by committee has its weaknesses.
I think being this abstract is good thing. There will be some weaknesses I am sure some real and more perceived. I think after doing generics it will be ready on the node side if things. Then nodes will be easy but initially a bit limited.
Keep the docs with the nodes declaration is good a thing and I have thought a bit about it but nothing really concrete.
If you have any good ideas please feel free to try them out. Of course the signatures will some give nice things to play with.
Today I leaning towards node_func
as a name, you are, after all, declaring that this function is node. The verbosity is appropriate.
I can still follow this and while it is becoming more abstract it is no more abstract than Blender's own usage of layout.operator layout.props conventions..and register_module(__name__)
. Once the convention is shown to work it will be adopted.
go with node_func
, as it will be doing more than merely registering the node.
@ly29 what about nodes that dont have output?
oh i see.
@node_func
def debug_print(data: Generic) -> None:
pprint.pprint(data)
nice :)
Node defaults, due to python requirement that parameters with defaults has to come after parameters without default values basically all input need defaults.
>>> def func(a=1, b):
... print(a,b)
File "<blender_console>", line 1
SyntaxError: non-default argument follows default argument
Therefore two special default values are supported.
None
which indicates that the parameter is optionalRequired
which indicates that without any link the layout won't compile. Required
will be defined in svtyping
@node_func(label="If")
def if_node(value: Integer = 1,
If: Generic = Required,
Else: Generic = Required,
) -> [("value", Generic)]:
pass
How to make nodes simple and clean to write.