[PyNodes] 2.66.6 Coding PyNodes?

Hi All,

With pynodes on the horizon for the next release I thought I would try to get some kind of example code working to leverage this new feature.

I copied this code from the developers pagehere.


import bpy
#http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes

from bpy.props import PointerProperty, StringProperty, BoolProperty, EnumProperty, \
IntProperty, FloatProperty, FloatVectorProperty, CollectionProperty

class MyCustomNode(bpy.types.Node):
    # Description string
    '''A custom node'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomNodeType'
    # Label for nice name display
    bl_label = 'Custom Node'
    # Icon identifier
    bl_icon = 'SOUND'
    # File path property stored in the node
    myStringProperty = bpy.props.StringProperty(subtype='FILE_PATH', default="//")
 
    # Custom enum property to select from a predefined list
    my_items = [
        ("DOWN", "Down", "Where your feet are"),
        ("UP", "Up", "Where your head should be"),
        ("LEFT", "Left", "Not right"),
        ("RIGHT", "Right", "Not left")
    ]
    myEnumProperty = bpy.props.EnumProperty(name="Direction", description="Just an example", items=my_items, default='UP')

    # the input value property to use for an unconnected socket
    my_input_value = bpy.props.FloatProperty(name="Size", default=5.0, subtype="FACTOR")

    @classmethod
    def poll(cls, tree):
        # this node is only available in trees of the 'CustomTreeType'
        return tree.bl_idname == 'CustomTreeType'

    def poll_instance(self, tree):
        return True

    def init(self, context):
        print("New node: ", self.name)
        #my_input = self.inputs.new("NodeSocketFloat", "My Input")
        #my_input.value_property = "my_input_value"

    def copy(self, node):
        print("New node ", self.name, "copied from", node.name)
        
    def free(self):
        print("Removing node: ", self.name)
        
    def update(self):
        print("Updating node: ", self.name)

    def draw_buttons(self, context, layout):
        layout.prop(self, "myStringProperty")
        layout.prop(self, "myEnumProperty")

    def draw_buttons_ext(self, context, layout):
        layout.label("These are extended node options")
        layout.prop(self, "myStringProperty")
        layout.prop(self, "myEnumProperty")
        layout.operator("node.my_custom_operator")

class MyCustomSocket(bpy.types.NodeSocket):
    # Description string
    '''Custom node socket type'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomSocketType'
    # Label for nice name display
    bl_label = 'Custom Node Socket'
    # Socket color
    bl_color = (1.0, 0.4, 0.216, 0.5)

class MyCustomTree(bpy.types.NodeTree):
    # Description string
    '''A custom node tree type that will show up in the node editor header'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomTreeType'
    # Label for nice name display
    bl_label = 'Custom Node Tree'
    # Icon identifier
    # Note: If no icon is defined, the node tree will not show up in the editor header!
    #       This can be used to make additional tree types for groups and similar nodes
    bl_icon = 'NODETREE'

    @classmethod
    def poll(cls, context):
        # typical shader node test for compatible render engine setting
        return context.scene.render.engine == 'Cycles'

bpy.utils.register_class(MyCustomNode)

The code does run and the class registers, but no new Node appears in the list to add to the Node window.

Does anyone know how to get this working so my new custom node will show up and work?

That page is a bit outdated. Check the “custom nodes” template in blender’s text editor.
Very least you need to register the tree type:
bpy.utils.register_class(MyCustomTree)

Even then I’m not sure how extending cycles would work at the moment. The poll method seems to hide your tree type entirely. If you remove that, it at least shows up in the node editor.

To add nodes to the add menu, your tree type needs a method:
def draw_add_menu(self, context, layout):
layout.label(“Hello World!”)
add_nodetype(layout, bpy.types.CustomNodeType)

Then you need this shortcut function somewhere in the code:
def add_nodetype(layout, type):
layout.operator(“node.add_node”, text=type.bl_label).type = type.bl_rna.identifier

Also note that custom sockets need a color function. Something along the lines of:
def draw_color(self, context, layout):
return self.bl_color

Thanks for mentioning the Custom Node template. After patching it, I got it to work. I can now add a custom node to the Node Editor.

Now to figure out what my node will do…!

Attachments


well this seems great and promising !!

does it connect data from C or from python objects (I mean assume you connect object A points as an input —>does that mean it will take the data from python or it will just take something like a pointer and access the underlying C data which should be faster in looping)

It accepts data from other nodes. But from within each node’s free event, you can reference the entire bpy.data structure. This example custom node moves the default cube when the frame changes.

Attachments

266_custom_pynode.blend (95.3 KB)

which graphicall version to use it

well nvm,I’ve tested it with 2.66 and it worked well
still need to know how fast it will interact with data like points

if you have time to test something like this
make a cube with 100,000 points and copy it
make one of the 2 cubes points = the other cube points position + a constant in any axis (so it is vertex i to vertex i)
and check the playback speed in object mode wireframe with VBO as this is the fastest mode

Does anyone know how to make this example node show up under the custom tree instead of the compositing tree?

How do you specify the ‘space type’ for a node?

By default, a node belongs to all trees. In the template, it’s handled by this bit, which should confine all nodes that descend from “MyCustomTreeNode” to only appear in the node editor “MyCustomTree”:

# Base class for all custom nodes in this tree type.# Defines a poll function to enable instantiation.
class MyCustomTreeNode :
    @classmethod
    def poll(cls, ntree):
        return ntree.bl_idname == 'CustomTreeType'

Does it not show up in the add menu in the custom tree?

did anyone saw the which changes have been done for the output material node
got an old nodes setup script and it`s is now giving an error on the material output line É

thanks

Does it not show up in the add menu in the custom tree?

@J_the_Ninja: No, it does not show up in the Add menu for the Custom Tree type. Instead it shows up under the Input menu under the Compositing Tree.

I am attaching the code, so far. It is basically the template with a few tweaks.

Attachments

266_custom_pynode.blend (94.9 KB)

I have custom data associated with textures. I have created the custom node for the data, and have tried to tie either an image selector or the CompositorNodeTexture node, for example, to the custom node. I have not been successful. I was even trying to make my custom node a derivative of the Compositor node. The route that shows promise in Blender is to create a group node, of which the Compositor node would be a part. In my case, it would be a part of a custom node group. I tried coding that, but have not been successful. I will state that this is stretching my limits of understanding. So… is there a way to do any of the things I’ve mentioned previously via the Python API?

So how do we add actuall functionality to the node? In the “update” event?
I haven’t yet seen a pynode yet which actually manipulates image data / input.

This is a great mistery for now. As far as I figured it out at the moment - the question is not how to process data (it can be done with update methods, as you’ve mentioned), but HOW to take this data from input sockets and, after processing, send it to outputs. No matter what I tried - i cant figure it out. There is simply no methods and properties to get data from socket.
Any thoughts?

They don’t have an exec() method or anything like that so you have walk the node tree yourself and compute the outputs based on the inputs. Or use them as a representation of the built-in nodes in your own renderer (or whatever) and convert them over as part of the export process.

There is some properties desctibed in http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes that is not in API currently. This is why, I think, Blender API simply not ready to work with PyNodes (yeah, weird), since there is no way to get data from sockets (at least I couldn’t figure it out)

Says that MyCustomSocket draw function need to have 5 args, found 4… (r56676)

Though it seems that is irregular call


def draw(self, context, layout, node, x):
        print(dir(x)) # Nothing there

compute the outputs based on the inputs

I guess that is the missing part. For example, a color output which represents an entire image? I can run code in the update() event, how do I assign a resulting value to an output?

That is the problem - you can’t

A long, long time ago, when I was working on my own implementation of pynodes, I had to do some serious hackery to get the input data into the node sockets. Blender itself doesn’t do this but passes it in as function arguments when it calls the (built in) nodes as part of the nodetree exec loop.

It wasn’t actually that hard to do, get the data from the passed in pointer and attach it to the socket but the main problem is that nodes/trees/sockets really weren’t designed to work this way. IIRC it gets its data from the connected output sockets and passes that into the function and then stores the computed data in its own output sockets, rinse and repeat…

Unfortunately all that work is gone due to The Makefile Incident™ and I haven’t really gotten around to seeing how Lucas did pynodes to figure out how hard it’d be to add an exec() function + socket data getter/setter functions + image buffer support (for comp nodes since the current image.pixels method is brutally slow and to make it compatible with PIL for eg) + node properties and a draw() function (this was one of the things that Just Worked when I was messing with them ;).

One of these days I’ll get around to figuring all this out again, it really isn’t that hard I just need to finish a couple other projects first and get all motivated to do some blender work.