Solved Cannot Connect Xpresso Nodes in Python

I am using the Python script manager to try and automate a few things. I am creating a hierarchy and I want to create an xpresso tag where the z offset of one cloner is driven by the z size of another cloner. Here's my code:

import c4d
from c4d import gui
import math

def main():
    cloner = c4d.BaseObject(1018544) # cloner id
    
    cloner[c4d.MG_LINEAR_OBJECT_POSITION,c4d.VECTOR_Y] = 0
    cloner[c4d.MG_LINEAR_OBJECT_POSITION,c4d.VECTOR_Z] = 500
    cloner[c4d.MG_LINEAR_COUNT] = 10
    cloner[c4d.MGCLONER_VOLUMEINSTANCES] = True
    
    doc.InsertObject(cloner)
    newnull = c4d.BaseObject(c4d.Onull)
    newnull.InsertUnder(cloner)
    internal_cloner = c4d.BaseObject(1018544) # cloner id
    internal_cloner.InsertUnder(newnull)
    internal_cloner[c4d.ID_MG_MOTIONGENERATOR_MODE] = 3 # 3 is grid array
    internal_cloner[c4d.MG_GRID_SIZE] = c4d.Vector(300, 200, 100)
    
    cube = c4d.BaseObject(c4d.Ocube)
    cube.InsertUnder(internal_cloner)
    cube[c4d.PRIM_CUBE_LEN] = c4d.Vector(20, 10, 100)
    
    xtag = c4d.BaseTag(c4d.Texpresso) # create the xpresso tag
    cloner.InsertTag(xtag)
    
    # http://mograph.net/board/index.php?/topic/26338-creating-xpresso-nodes-with-python-script/
    nodemaster = xtag.GetNodeMaster()
    node1 = nodemaster.CreateNode(nodemaster.GetRoot(), c4d.ID_OPERATOR_OBJECT, None, 100, 100)
    node1[c4d.GV_OBJECT_OBJECT_ID] = internal_cloner
    node1out = node1.AddPort(c4d.GV_PORT_OUTPUT, (c4d.MG_GRID_RESOLUTION, c4d.VECTOR_Z))
    
    node2 = nodemaster.CreateNode(nodemaster.GetRoot(), c4d.ID_OPERATOR_OBJECT, None, 300, 100)
    node2[c4d.GV_OBJECT_OBJECT_ID] = cloner
    node2in = node2.AddPort(c4d.GV_PORT_INPUT, (c4d.MG_LINEAR_OBJECT_POSITION, c4d.VECTOR_Z))
    
    node1out.Connect(node2in)
    
    c4d.EventAdd() # refreshed viewport to see changes automatically
    # id = op.GetType()

if __name__=='__main__':
    main()

Unfortunately, this does not work and gives me an "AttributeError: 'NoneType' object has no attribute 'Connect'.
When looking at the results, you can see that the first cloner never had its output port created:
alt text
What I'm wanting is this:
alt text
Can you please help me understand what I'm doing wrong in this code? Thank you.

"AttributeError: 'NoneType' object has no attribute 'Connect'

means that the variable node1out is a NoneType; it is not what you think it is. That means that node1.AddPort() has returned None.

Why AddPort() has returned None, I don't know. I can only speculate that one has to construct a more complex DescID handling MG_GRID_RESOLUTION.

Hi, here in Cinema 4D S22 defining the type of the cloner I don't have anymore an issue

import c4d
from c4d import gui
import math

def main():
    # Build Parent Cloner
    cloner = c4d.BaseObject(1018544)
    cloner[c4d.ID_MG_MOTIONGENERATOR_MODE] = c4d.ID_MG_MOTIONGENERATOR_MODE_LINEAR
    doc.InsertObject(cloner)

    # Build Null
    newnull = c4d.BaseObject(c4d.Onull)
    newnull.InsertUnder(cloner)

    # Build Children Cloner
    internal_cloner = c4d.BaseObject(1018544)
    internal_cloner[c4d.ID_MG_MOTIONGENERATOR_MODE] = c4d.ID_MG_MOTIONGENERATOR_MODE_GRIDARRAY
    internal_cloner.InsertUnder(newnull)
    internal_cloner.Message(c4d.MSG_MENUPREPARE, doc)

    # Cube
    cube = c4d.BaseObject(c4d.Ocube)
    internal_cloner.Message(c4d.MSG_MENUPREPARE, doc)
    cube.InsertUnder(internal_cloner)

    # Xpresso Tag
    xtag = c4d.BaseTag(c4d.Texpresso)
    cloner.InsertTag(xtag)

    # Setup Parent Cloner
    cloner[c4d.MG_LINEAR_OBJECT_POSITION, c4d.VECTOR_Y] = 0.0
    cloner[c4d.MG_LINEAR_OBJECT_POSITION, c4d.VECTOR_Z] = 500.0
    cloner[c4d.MG_LINEAR_COUNT] = 10
    cloner[c4d.MGCLONER_VOLUMEINSTANCES_MODE] = c4d.MGCLONER_VOLUMEINSTANCES_MODE_RENDERINSTANCE

    # Setup Children Cloner
    internal_cloner[c4d.MG_GRID_SIZE] = c4d.Vector(300, 200, 100)

    # Setup Cube
    cube[c4d.PRIM_CUBE_LEN] = c4d.Vector(20, 10, 100)

    # Setup Xpresso
    # http://mograph.net/board/index.php?/topic/26338-creating-xpresso-nodes-with-python-script/
    nodemaster = xtag.GetNodeMaster()
    node1 = nodemaster.CreateNode(nodemaster.GetRoot(), c4d.ID_OPERATOR_OBJECT, None, 100, 100)
    node1[c4d.GV_OBJECT_OBJECT_ID] = internal_cloner
    gridResParam = c4d.DescID(c4d.DescLevel(c4d.MG_GRID_RESOLUTION, c4d.DTYPE_VECTOR,0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL,0))
    node1out = node1.AddPort(c4d.GV_PORT_OUTPUT, gridResParam)

    node2 = nodemaster.CreateNode(nodemaster.GetRoot(), c4d.ID_OPERATOR_OBJECT, None, 300, 100)
    node2[c4d.GV_OBJECT_OBJECT_ID] = cloner
    posParam = c4d.DescID(c4d.DescLevel(c4d.MG_LINEAR_OBJECT_POSITION, c4d.DTYPE_VECTOR,0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL,0))
    node2in = node2.AddPort(c4d.GV_PORT_INPUT, posParam)

    # Connect
    if node1out is not None and node2in is not None:
        node1out.Connect(node2in)

    c4d.EventAdd() # refreshed viewport to see changes automatically

if __name__=='__main__':
    main()

And as pointed by @PluginStudent the problem was because the AddPort fails. And I guess it failed because at the time you ask for the port creation since no explicit mode was defined in the Cloner the XPresso description was not properly fed so this GvPort didn't exist.

Cheers,
Maxime.