Custom Python Nodes: How to convert nodes to python script?

Hi,

I am learning custom python node trees in blender. I got custom nodes with different properties so far.
But how do I execute code with them?

I know it is just an UI interface and I have to build the backend. But where do I start? If for example I want to write some blender data to an external file (XML) where to put that line of code so that it is associated to that node?

If someone experienced enough can point me in the right direction, it would be really helpful.

Thanks in advance,
-Sayan.

1 Like

Here I modified the custom nodes template to support writing to a file.

Also I think that the filepath must be escaped so the \ character does not count as an escape…

# Derived from the Node base type.
class MyCustomNodeSave(Node, MyCustomTreeNode):
    '''Custom save node'''
    bl_idname = 'CustomNodeSaveType'
    bl_label = 'Custom File Save'

    my_string_prop: bpy.props.StringProperty(name='Path')

    def init(self, context):
        self.inputs.new('NodeSocketString', "Data")
        self.outputs.new('NodeSocketString', "Data")

    def draw_buttons(self, context, layout):
        layout.label(text="Node Settings")
        layout.prop(self, "my_string_prop")

    def draw_buttons_ext(self, context, layout):
        layout.prop(self, "my_string_prop")
        
    def update(self):
        import time
        print('updated', str(time.time()))

        print('links')
        print(self.inputs['Data'].links)
        print('')
        
        if len(self.inputs['Data'].links) == 0:
            return

        link = self.inputs['Data'].links[0]
        print('receive from node: ', link.from_node.name, 'socket: ', link.from_socket.name)
        
        print('value: ', link.from_socket)
        # alternative way to get socket: link.from_node.outputs[link.from_socket.name]
        
        print(link.from_socket.default_value)
        
        file = open(self.my_string_prop, 'w')
        file.write(link.from_socket.default_value)
        file.close()

writetofilenode.txt (7.7 KB)

Thanks a lot, I appreciate the effort you put into preparing the full code. There is so less info on this topic, this is going to help me a lot. Already I can understand some of the flaws in the way I was thinking about it.

Will be keeping this as a reference as I progress further! Hope you don’t mind if I post some doubts as I analyze the code more.

Thanks again! Have a great day :grinning:

Regards,
-Sayan.

I have done some peeking into “Svertchok” and “Animation Nodes” just to get inspiration, not exactly that is 100% essential, but it can clear confusion in many ways. As for example I could think of this system of writing in files, with using a modal operator and checking an activation property, but rather instead with this mindset just throw the code into the the update and is done. :wink:

P.S. As an extra hint, you can store the hash ID of the text written in a property, only write the new text if the new hash is different. Unless you want to have a checkbox to always write no matter what. Lots of room for optimization.

These are some great tips, I can see so many possible ways to do it. I have progressed quite a lot since. I would love to hear your thoughts on node tree traversal. What algorithm or design do you generally follow to convert the tree to code/instructions?

Thanks for your valuable input so far! :slightly_smiling_face:

Regards,
-Sayan.

You can think that the node tree as a concept is closer to functional programming rather than procedural programming.

If you think in the most simple case that you have a linear node graph with nodes like this Input -> One -> Two -> Three -> Output you would express it this way with python.

def Input():
	return "Input "

def One(x):
	return x + "|> One "

def Two(x):
	return x + "|> Two "

def Three(x):
	return x + "|> Three "

def Output(x):
	print(x)

Output(
	Three(
		Two(
			One(Input())
		)
	)
)

And it produces the output like this: Input |> One |> Two |> Three, as for the sake of the example the Output is the real output to console, but for example say this was something more useful like string manipulation / validation sequence / math.

Sidenote: This is proof that following a clear and concise paradigm, like Functional Programming, you can do it even in Python. Provided that you stay within these principles your code is considered functional. Regardless of what the actual programming language is.

With this mindset the next of the theory can be completed with the Railway Oriented Programming design. However the design is not strict, is more of a guideline on how to structure a program. As for example I always find doing math in nodes too boring and cumbersome because just so you add 3 numbers it takes an impressive amount of 3 nodes (two values and one math operation). I would prefer in this case just to have one node instead, with two input fields and the math operation as a menu box.
https://www.google.com/search?q=railway+based+programming

I hope this makes sense, I hope I explained it quite easily to understand, otherwise if you need any clarification you can mention it. :nerd_face:

I know a few things about functional programming but I haven’t heard about Railway Oriented Programming before, thanks for the pointers!

Your explanation makes full sense. One thing I would like you to expand on would be, would you approach a non-linear graph in a similar way? I can think of a binary tree search approach, but I am not sure if I need to go that complex.

Also, since I haven’t implemented functional programming in python before. Is there any better alternative than writing it like this:

Output(
	Three(
		Two(
			One(Input())
		)
	)
)

Thanks! I am grateful to all the knowledge you are sharing here. Means a lot to me :slightly_smiling_face:

would you approach a non-linear graph in a similar way

Yeah the same idea applies, the code works the same even if you have conditions or loops.

import random

def GetRandom():
  r = random.random()
  print('random is', r)
  return r

def Condition(condition, successA, successB):
  if condition():
    return successA
  else:
    return successB

def GetMessage(msg):
  return msg

def Print(msg):
  print(msg)

Print(
  Condition(
    lambda: GetRandom() < 0.5,
    GetMessage("Head"),
    GetMessage("Tails")
  )
)

One of the most difficult problems however here is the organization of this thing so it can be more reasonable to construct. Obviously as you cleverly point out there is no way to create maintainable code like this if is Lisp-ified to the max.

In FP monads are used, so in this case there would be Either or Option nodes used. In NodeGraphs for example is something else. Pretty much exactly as done in Blender.

You can consider that each function is turned to a node:Node instead since is ObjectOriented design. The actual code is evaluated during the def update(self): method and you don’t have to pass prefixed arguments and return types (which would force you to overload the update method 5000 times to hit all combinations), you can rely on input/output slots.

A really good idea is to see if there are more flow graph related projects. Since you might peek into Blender source but not quite have tremendous C++ experience so you can instantly decrypt it’s workings.
https://github.com/PaulSchweizer/flowpipe