File output node - add socket?

Hi,

Here’s my issue:
I’m trying to create simple script that will add file output node to my node tree and connect all of the outputs of RenderLayer input node to the inputs of the File output node.

The problem I have is to add input sockets to the File Output node.
When I try to use: boy.ops.node.output_file_add_socket() in my script - it fails. It seems that it can’t be used that simple. I either got something about “wrong context” or it does nothing even if my newly created node is active and selected.

Is there any way to add sockets to “File Output” node via script?

@ideasman42 Thanks. That’s not exactly what I wanted, but you gave me the answer on IRC, so I got the solution. Thank you.
As soon as I get it all working I’ll give the full info here.

—edit— 2012-11-14

Solution given by ideasman42 is as follows:

bpy.ops.node.output_file_add_socket() needs the proper context.
Two issues:

  1. It can be executed only when we are in node editor, i.e: mouse is hovered over node editor panel.
  2. We need to specify the file output node as a part of a context.

Ad. 1
When we use this operator in the script and want to execute it directly from text editor window by hitting atl-P or “Run Script” button - it simply won’t work. To solve this issue - we need to “wrap” it in operator that can be executed when we are in node editor:


class NodeFileOutputSocket(bpy.types.Operator):
    bl_idname = "node.add_socket"
    bl_label = "Add File Output Socket"


    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'


    def execute(self, context):
        main(context)
        return {'FINISHED'}




def register():
    bpy.utils.register_class(NodeFileOutputSocket)




def unregister():
    bpy.utils.unregister_class(NodeFileOutputSocket)




if __name__ == "__main__":
    register()

So those will be last lines of our code. Before that we need to define “main” function where we will address the second issue, i.e - passing our node to the context.


def main(context):
    scn = context.scene
    tree = svn.node_tree
    nodes = tree.nodes
    node = nodes['File Output']  # Here we specify the file output node that we want to add input socket to.
    C = context.copy()  # Create copy of context to be used for bpy.ops.node.output_file_add_socket()
    C["node"] = node  # We pass our node into the context
    bpy.ops.node.output_file_add_socket(C)  # here's where the magic happens

The whole code looks like this:


import bpy

def main(context):
    scn = context.scene
    tree = svn.node_tree
    nodes = tree.nodes
    node = nodes['File Output']  # Here we specify the file output node that we want to add input socket to.
    C = context.copy()  # Create copy of context to be used for bpy.ops.node.output_file_add_socket()
    C["node"] = node  # We pass our node into the context
    bpy.ops.node.output_file_add_socket(C)  # here's where the magic happens

class NodeFileOutput(bpy.types.Operator):
    bl_idname = "node.add_socket"
    bl_label = "Add File Output Socket"


    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'


    def execute(self, context):
        main(context)
        return {'FINISHED'}




def register():
    bpy.utils.register_class(NodeFileOutput)




def unregister():
    bpy.utils.unregister_class(NodeFileOutput)




if __name__ == "__main__":
    register()



Hover the mouse over this text, alt-P, then hover the mouse over node editor, space -> “Add File Output Socket”, BAM.
By the way: It’s really annoying that we need to write so many lines just to add a socket.
IMHO it would be great if we could say something like:



# pseudo code

node = node = nodes['File Output']  # provided we have earlier specified that nodes stands for node tree nodes of our scene
node.inputs.new()  # there's no such method, but it would be great if it existed


@ideasman42 Thanks. That’s not exactly what I wanted, but you gave me the answer on IRC, so I got the solution. Thank you.

Basing on ideasman42’s suggestions I got this:

bpy.ops.node.output_file_add_socket() needs the proper context.
Two issues:

  1. It can be executed only when we are in node editor, i.e: mouse is hovered over node editor panel.
  2. We need to specify the file output node as a part of a context.

Ad. 1
When we use this operator in the script and want to execute it directly from text editor window by hitting atl-P or “Run Script” button - it simply won’t work. To solve this issue - we need to “wrap” it in operator that can be executed when we are in node editor:


class NodeFileOutputSocket(bpy.types.Operator):
    bl_idname = "node.add_socket"
    bl_label = "Add File Output Socket"


    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'


    def execute(self, context):
        main(context)
        return {'FINISHED'}




def register():
    bpy.utils.register_class(NodeFileOutputSocket)




def unregister():
    bpy.utils.unregister_class(NodeFileOutputSocket)




if __name__ == "__main__":
    register()

So those will be last lines of our code. Before that we need to define “main” function where we will address the second issue, i.e - passing our node to the context.


def main(context):
    scn = context.scene
    tree = scn.node_tree
    nodes = tree.nodes
    node = nodes['File Output']  # Here we specify the file output node that we want to add input socket to.
    C = context.copy()  # Create copy of context to be used for bpy.ops.node.output_file_add_socket()
    C["node"] = node  # We pass our node into the context
    bpy.ops.node.output_file_add_socket(C)  # here's where the magic happens

The whole code looks like this:


import bpy

def main(context):
    scn = context.scene
    tree = svn.node_tree
    nodes = tree.nodes
    node = nodes['File Output']  # Here we specify the file output node that we want to add input socket to.
    C = context.copy()  # Create copy of context to be used for bpy.ops.node.output_file_add_socket()
    C["node"] = node  # We pass our node into the context
    bpy.ops.node.output_file_add_socket(C)  # here's where the magic happens

class NodeFileOutput(bpy.types.Operator):
    bl_idname = "node.add_socket"
    bl_label = "Add File Output Socket"


    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'


    def execute(self, context):
        main(context)
        return {'FINISHED'}




def register():
    bpy.utils.register_class(NodeFileOutput)




def unregister():
    bpy.utils.unregister_class(NodeFileOutput)




if __name__ == "__main__":
    register()



Hover the mouse over this text, alt-P, then hover the mouse over node editor, space -> “Add File Output Socket”, BAM.
By the way: It’s really annoying that we need to write so many lines just to add a socket.
IMHO it would be great if we could say something like:



# pseudo code

node = nodes['File Output']  # provided we have earlier specified that nodes stands for node tree nodes of our scene
node.inputs.new()  # there's no such method, but it would be great if it existed


BartekSkorupa, I don’t understand your solution, could you explaint it deeply? =)
I want just to have a script, which connects my Render Layer nodes to one File Output node (OpenEXR multilayer). Most of the script wasn’t tricky, but creating additional file socket is. What would you advise to solve this?
As I understand from your post, your code makes function, which executes while current context is node editor (and so is required by bpy.ops.node.output_file_add_socket):

Hover the mouse over this text, alt-P, then hover the mouse over node editor, space -> “Add File Output Socket”, BAM.

And I want script to do all the job, whithout my interaction, except pressing Alt+P. =) Is it possible?

This is outdated, now you can do:
output_node.layer_slots.new("my_new_input_socket")

as mentioned here