Hi @woodstar
There's definitely a way! What did you want to connect in Xpresso with Python? I wrote a script that does some of what you described: it prompts the user for a camera name, then creates an Xpresso rig based on that. The rig connects a user data checkbox 'Follow Target' to a couple of properties in the rig (Target tag's 'Enable' and the camera's 'Use Target Object''s checkbox for setting the Focus Distance). That would allow you to follow an object for some of the camera move and then keyframe it off.

"""
Name-US:Xpresso Camera Rig
Description-US:Creates an Xpresso Camera Rig
author:blastframe
credits:
Creating User Data - https://www.cineversity.com/wiki/Python%3A_User_Data/
Python Xpresso - https://www.cineversity.com/vidplaytut/xpresso_maker_overview
"""
import c4d
from c4d import gui
def create_user_data_group(obj, name, parentGroup=None, columns=None, shortname=None):
# see Creating User Data url above
if obj is None: return False
if shortname is None: shortname = name
bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = name
bc[c4d.DESC_SHORT_NAME] = shortname
bc[c4d.DESC_TITLEBAR] = 1
if parentGroup is not None:
bc[c4d.DESC_PARENTGROUP] = parentGroup
if columns is not None:
bc[22] = columns
return obj.AddUserData(bc)
def create_user_data_bool(obj, name, val=True, parentGroup=None):
# see Creating User Data url above
if obj is None: return False
bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_BOOL)
bc[c4d.DESC_NAME] = name
bc[c4d.DESC_SHORT_NAME] = name
bc[c4d.DESC_DEFAULT] = val
bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON
if parentGroup is not None:
bc[c4d.DESC_PARENTGROUP] = parentGroup
element = obj.AddUserData(bc)
obj[element] = val
return element
def connect_xpresso(camera,camera_truck_ctrl,targetTag):
# Xpresso documentation:
# https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d.modules/graphview/index.html
xtag = camera_truck_ctrl.MakeTag(c4d.Texpresso) # create Xpresso tag
doc.AddUndo(c4d.UNDOTYPE_NEW, xtag)
# A Graph View Node Master stores a collection of Graph View Nodes.
gv = xtag.GetNodeMaster()
# create node for the camera truck control
camera_truck_ctrlNode = gv.CreateNode(parent=gv.GetRoot(), id=c4d.ID_OPERATOR_OBJECT, insert=None, x=100, y=0)
doc.AddUndo(c4d.UNDOTYPE_NEW, camera_truck_ctrlNode)
doc.AddUndo(c4d.UNDOTYPE_CHANGE, camera_truck_ctrlNode)
udPort = None
# create output port for the user data boolean (from Rick Barrett script)
for id, bc in camera_truck_ctrl.GetUserDataContainer():
if bc[c4d.DESC_CUSTOMGUI] is not None and \
bc[c4d.DESC_CUSTOMGUI] is not c4d.CUSTOMGUI_SEPARATOR:
doc.AddUndo(c4d.UNDOTYPE_CHANGE,camera_truck_ctrlNode)
udPort = camera_truck_ctrlNode.AddPort(c4d.GV_PORT_OUTPUT, id)
# create node for the camera Target tag
targetNode = gv.CreateNode(gv.GetRoot(), c4d.ID_OPERATOR_OBJECT, insert=None, x=300, y=0)
# get DescID for Target tag's Enable property
targetEnabled = c4d.DescID(c4d.DescLevel(c4d.EXPRESSION_ENABLE));
doc.AddUndo(c4d.UNDOTYPE_NEW, targetNode)
# after creating node we need to give it an object
targetNode[c4d.GV_OBJECT_OBJECT_ID] = targetTag
# add Target node Enabled input port
enablePort = targetNode.AddPort(c4d.GV_PORT_INPUT, targetEnabled)
# create node for the camera
cameraNode = gv.CreateNode(parent=gv.GetRoot(), id=c4d.ID_OPERATOR_OBJECT, insert=None, x=300, y=100)
# after creating node we need to give it an object
cameraNode[c4d.GV_OBJECT_OBJECT_ID] = camera
# add camera input port for using the Target tag's object as the Focus Object
useTargetObjectPort = cameraNode.AddPort(c4d.GV_PORT_INPUT, c4d.CAMERAOBJECT_USETARGETOBJECT)
# connect the ports
udPort.Connect(enablePort)
udPort.Connect(useTargetObjectPort)
# refresh the Graph View
c4d.modules.graphview.RedrawMaster(gv)
def main(doc):
doc.StartUndo()
camera_truck_ctrl = c4d.BaseObject(c4d.Osplinenside) # Create camera_truck_ctrl control
camera_truck_ctrl[c4d.ID_BASEOBJECT_USECOLOR] = 2 # turn on display color
camera_truck_ctrl[c4d.ID_BASEOBJECT_COLOR] = c4d.Vector(0,1,1) # set display color
doc.InsertObject(camera_truck_ctrl)
doc.AddUndo(c4d.UNDOTYPE_NEW, camera_truck_ctrl)
cameraControls = create_user_data_group(camera_truck_ctrl,"Camera Controls",c4d.DescID(0)) # create user data group
followTargetBool = create_user_data_bool(camera_truck_ctrl,"Follow Target",True,cameraControls) # Follow Target boolean checkbox
camera = c4d.BaseObject(c4d.Ocamera) # Create camera
name = gui.InputDialog("What would you like to name your camera?", "Camera") # prompt user for name
camera.SetName(name)
camera_truck_ctrl.SetName("%s_con+"%name) # use camera name to name control
targetTag = camera.MakeTag(c4d.Ttargetexpression) # create Target tag
doc.AddUndo(c4d.UNDOTYPE_NEW, targetTag)
focusObject = c4d.BaseObject(c4d.Onull) # Create focus object
focusObject.SetName("%s Target"%name)
focusObject[c4d.ID_BASEOBJECT_USECOLOR] = 2 # turn on display color
focusObject[c4d.ID_BASEOBJECT_COLOR] = c4d.Vector(1,0,0) # set display color
focusObject[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] = 500 # set focus object's position Z to 500
focusObject[c4d.NULLOBJECT_DISPLAY] = 13 # set null target to display as sphere
focusObject[c4d.NULLOBJECT_RADIUS] = 30 # set sphere radius to 30
focusObject[c4d.NULLOBJECT_ORIENTATION] = 1 # set sphere plane to XY
targetTag[c4d.TARGETEXPRESSIONTAG_LINK] = focusObject # assign focus object to Target tag object link
# insert objects to document
doc.InsertObject(focusObject)
doc.AddUndo(c4d.UNDOTYPE_NEW, focusObject)
doc.InsertObject(camera_truck_ctrl)
doc.AddUndo(c4d.UNDOTYPE_NEW, camera_truck_ctrl)
camera.InsertUnder(camera_truck_ctrl) # parent to camera_truck_ctrl
doc.AddUndo(c4d.UNDOTYPE_NEW, camera)
connect_xpresso(camera,camera_truck_ctrl,targetTag) # create Xpresso connections
doc.SetActiveObject(camera_truck_ctrl,c4d.SELECTION_NEW) # select camera truck control
c4d.EventAdd()
doc.EndUndo()
if __name__=='__main__':
main(doc)