[Bf-python] 2nd thoughts on the node api
Toni Alatalo
antont at kyperjokki.fi
Sun Dec 10 15:41:33 CET 2006
i'm having second thoughts on the node api. after all, we all know that
things should be as simple as possible (but not simpler).
initially, during Orange when Ton was making the node system, i was
thinking that nodes are functions: they take input, and give output,
based on the processing inside. so was sketching something like:
def mynode(color, amount):
image = x(color)(y(amount)z(random))
return image
nice, huh?
the problem there is, though, that i don't know if Python function
signatures can be introspected, for Blender to be able to know the node
definition. so, as Python has the nice way of having information about
functions by making them with classes, the follow-up was something like:
class MyNode(Blender.Node):
inputs = {'color': RGBA, 'amount': NUMBER}
outputs = [IMAGE]
def __call__(self):
image = x(self.color)y(self.amount)(z(random))
return image
not bad? so this is what i was advocating when Jestie took up the job,
and basically this is what we have now, and i guess it is really cool.
the concerns from some scripters about the complexity -- need to have a
class, use the obscure __call__ method etc -- we have silenced with the
idea that there would be GUI in the script node that takes care of that:
you add inputs & outputs by clicking new sockets with the mouse, and
then there is a script editing thing where you just put your function,
which is then put to __call__ behind the scenes and you don't have to
know. that may be good, and perhaps we even get it done :)
but now i started thinking that perhaps all that is also wrong / not
sufficient. we should not restrict scripting to such a rigid structure
unless it is really needed, and i don't think it is. so how about going
back to something like this, that was already implemented earlier i think?
inputs = [RGBA, NUMBER]
outputs = [IMAGE]
def xyz(color, amount):
image = x(color)y(amount)(z(random))
return image
NodeType("MyNode", inputs, outputs, xyz)
#or register_nodetype("MyNode", inputs, outputs, xyz)
there the users dont have to know about classes and magic methods, and
the system is straightforwardly flexible for potential weird complex
needs too, 'cause you can just get the parts of the nodedef: socket
declarations and the processing func from anywhere and put them there.
so now i regret / take back my arguments againts that technique -- sorry.
now, i don't know if it makes sense in practice, but at least for me as
an academic / stupid / whatever question is whether that NodeType /
registration thing could be just plugged in to complement the current
implementation, where you introduce new nodetypes by implementing a
Node. i mean just as an alternative way of declaring the own node class.
BTW i don't know if the c implementation actually uses the inheritance
mechanism there, or whether it would actually be just better to use duck
typing and not require the inheritance - a node could be of any class
that provides the node interface. but if the registration and even
execution mechanism uses inheritance somehow i guess it is good. but
back to the esoteric question: can we make class factories easily in
Python? something like:
def register_nodetype(name, inputs, outputs, impl):
nodetype = type(..)
#here my knowledge ends
nodetype.__init__ .. should somehow use the given input&output declrs
nodetype.__call__ = impl #i have no idea if
class type(object)
| type(name, bases, dict) -> a new type
but it seems i still don't understand what types and classes are in python:
In [43]: t = type("MyNode", (Node), {})
TypeError: type() argument 2 must be tuple, not classobj
t = type("MyNode", (), {})
works but then i don't know what to do :)
am writing this offline so did not read any docus - may later, but am
not at all surprised if that that type creation magic is not needed, but
the users-dont-need-to-know-classes way can be implemented on the c side
without such trickery too. was just having a bit of fun with that
thought, and would still like to know how to make such class factories :)
ah - am sorry for the long writeup, but now i finally realized that
classes are just objects too and you can put the declarations anywhere:
In [46]: def c():
....: class C:
....: def __init__(self):
....: print "yo!"
....: return C
In [47]: k = c()
In [48]: k()
yo!
Out[48]: <__main__.C instance at 0x00EC5D78>
so perhaps a node class factory can be made straightforwardly with that
.. yes, that seems to work! gotta love Python :)
def make_nodetype(name, inp, out, impl):
class NewNode(Blender.Node):
inputs = inp
outputs = out
__call__ = impl
return NewNode
an executable example with a self-test is attached and on-line at
http://an.org/blender/nodefactory.py - it creates a new nodetype using
that class factory function, instanciates one such node, and executes it
(does 2*3 ==> 6 :)
so should we just add such a utility func as an alternative node
registration way? (for people who dont want to know about classes) .. i
just hope that the c impl allows such a trick. one question is the class
name - dunno how it uses that actually, and whether it could be set
(with the type() function it seemed you can..)
a counterargument could be that an API should provide only one way of
doing things and support that, but i think that point is moot here: if
that works, it is just a different way of doing the same thing, and
libraries often have utility functions as alternative interfaces to the
same underlying things that can be used directly too.
~Toni
More information about the Bf-python
mailing list