Skip to main content

Build nodes trees in Blender more elegantly with code

Project description

nodebpy

Run Tests

A package to build node trees in blender more elegantly with python code. Geometry Nodes, Shader Nodes and Compositor nodes are all fully supported. Look at the documentation for more examples.

Creating Nodes With Code

A text-based version of nodes should bring the convenience of writing code with IDE auto-completion, type hinting, with overall compactness and readability, while staying as close as possible to what building a node tree via the GUI feels like.

from nodebpy import geometry as g

with g.tree("AnotherTree", collapse=True) as tree:
    rotation = (
        g.RandomValue.vector(min=-1, seed=2)
        >> g.AlignRotationToVector()
        >> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
    )

    _ = (
        tree.inputs.integer("Count", 10)
        >> g.Points(position=g.RandomValue.vector(min=-1))
        >> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
        >> g.SetPosition(
            position=g.Position() * 2.0 + (0, 0.2, 0.3),
            offset=(0, 0, 0.1),
        )
        >> g.RealizeInstances()
        >> g.InstanceOnPoints(g.Cube(), instance=...)
        >> tree.outputs.geometry("Instances")
    )

Nodes are created by instantiating their classes. The node tree they are added to is determined by the context the code is executed in (while inside with g.tree():). The interface for the node tree is created with tree.inputs and tree.inputs, adding the sockets and returning the input or output socket for linking with other nodes.

Nodes are linked by overloading the >> operator, to link from the previous node on the left to the input on the right. Suitable socket pairs are automatically selected or explicitly supplied.

The layout / arrangement of the node tree is not important to Blender's evaluation of it - but an automatic layout algorithm is potentially applied upon exiting the node tree context.

nodebpy and the >> operator

In nodebpy we use the >> operator to link from one node or socket into another. This should feel and behave much like the Alt + Right Click drag between nodes in Node Wrangler. It will use some smart logic to match the most compatible sockets between the nodes, but if you ever want to be explicit you do so. The input and output sockets of a node are accessible as properties via the i.* and o.* prefixes, or you can use the ... placeholder to specify the particular input to be user, or pass in the previous node as a named argument.

# vector output will be linked into the first vector input (position)
g.Vector() >> g.SetPosition()
# vector output will be linked into the offset input
g.Vector() >> g.SetPosition(offset=...)
g.SetPosition(offset=g.Vector())

The >> operator will always look for the most compatible sockets first (matching data types) before looking for other compatible but not identical socket data types to link. If a compatible match can't be found an error will be thrown.

Contexts

What node tree or node tree interface we are currently editing is determined based on contexts. Instantiating a node class outside of a tree context will throw an error. The easiest way to enter and exit a tree context is to use the with statement.

Each time you instantiate a node class, a new node will be created and added to the current tree. If these nodes are given as arguments to other nodes or used with the >> operator, they will be automatically linked to the appropriate sockets.

Nodes

Documentation for all of the nodes can be found in the API Reference. This is mostly built automatically from the existing Blender node classes.

Every node has all of it's input sockets and enum options exposed as arguments to the class constructor. Input sockets are prefixed with .i.* and output sockets are prefixed with .o.*. Properties that aren't exposed as sockets are available as class properties. Many properties are also available as class methods for convenience when constructing.

The basic math operators also automatically add relevant nodes with their operations and values set.

# operation is exposed as a property
math = g.Math(1.0, 2.0, operation='ADD')
math.operation = "SUBTRACT"

# operation can be chose as a class method
math = g.Math.subtract(1.0, 2.0)
math = g.Value(1.0) - 2.0
math = g.Math.add(1.0, 2.0)
math = g.Value(1.0) + 2.0
# the 3.0 + 2.0 is evaluated as regular python code first,
# so the result with be a Math.add(g.Value(1.0), 5.0)
math = g.Value(1.0) 3.0 + 2.0

# these are equivalent, the g.Math.multiply is automatically added
g.Value(1.0) * 2
g.Math.multiply(g.Value(1.0), 2.0)

Design Considerations

The top priority of nodebpy has been type hinting and IDE auto-complete. Typical tooling that supports autoring regular python code should also support the authoring of node trees. Much like databpy, this started as an internal tool used inside of molecularnodes but has since been broken out into it's own separate project. This projects is robustly typed and tested, with the intent that it can be used internally for multiple other add-ons and projects.

  • Node classes are named after nodes 'Random Value' -> RandomValue()
  • Node 'subtypes' and methods should be accessible via dot (.) for easier IDE auto-complete and authoring:
    • RandomValue(data_type="FLOAT_VECTOR") -> RandomValue.vector()
  • Node properties are available on the top level, with inputs and outputs available behind .i.* and .o.* accessors
  • Inputs and outputs from a node are prefixed with i.* and o.*:
    • AccumulateField().o.total returns the output Total socket
    • AccumulateField().i.value returns the input Value socket

Building

Most of the code for classes are generated automatically with the generate.py script. Some nodes are manually specified in the src/nodebpy/nodes/geometry/manual.py if they require special handling.

Run the build & format script as such:

uv run generate.py && uvx ruff format && uvx ruff check --fix

Other Projects

There are several other notable projects which have also attempted interfacing with node trees via code. They mostly fit into two categories of either storing & retrieving node trees via code (.json or the bpy API), or authoring of node trees with custom API and syntax. This project mostly fits in to the latter category.

Storing node trees as code:

Converting node trees to the python API calls or .json is great to have a robust storage method, but this approach falls down in human authorability / readability. These projects are great for storage but less useful when wanting to write / generate node trees froms scratch.

Authoring node trees with code:

Two previous projects have made similar approaches to authoring node trees. geometry-script also auto-generated most of it's type hinting, code and docs. It uses the approach of method chaining with the . operator, but obfuscates some of the non-linear way of building node trees.

The other project geonodes uses a similar context system for creating and authoring node trees, but doesn't use the same method of exposing each individual node as it's own class that nodebpy does.

I personally found both of their APIs to not quite fit how I wanted to work, leading to the creation of nodebpy. In comparison, this project is also the only one that is also distributed on PyPi and insallable via pip for easier use in other projects.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nodebpy-0.17.0.tar.gz (192.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nodebpy-0.17.0-py3-none-any.whl (227.4 kB view details)

Uploaded Python 3

File details

Details for the file nodebpy-0.17.0.tar.gz.

File metadata

  • Download URL: nodebpy-0.17.0.tar.gz
  • Upload date:
  • Size: 192.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nodebpy-0.17.0.tar.gz
Algorithm Hash digest
SHA256 ef1992c2f35a4255dfa8efaf996d6897eb4f87410e5da81da81720b43195af3f
MD5 9582bee75bfe1934c899e593a4ab8456
BLAKE2b-256 0d36fc0a54c6510c772900f886086fb0faee4f2198240ca27fedb96fbd7d188d

See more details on using hashes here.

Provenance

The following attestation bundles were made for nodebpy-0.17.0.tar.gz:

Publisher: pypi.yml on BradyAJohnston/nodebpy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nodebpy-0.17.0-py3-none-any.whl.

File metadata

  • Download URL: nodebpy-0.17.0-py3-none-any.whl
  • Upload date:
  • Size: 227.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nodebpy-0.17.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cfa3459d19cf933a90dc9b430d537e0d13220c10c929daa428258fa212f86097
MD5 db6147383d39a032cff39881922de55e
BLAKE2b-256 eff583d76662c3162fdc1040426a1436393693b0be419b6fee2bb7edd2adaf96

See more details on using hashes here.

Provenance

The following attestation bundles were made for nodebpy-0.17.0-py3-none-any.whl:

Publisher: pypi.yml on BradyAJohnston/nodebpy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page