How to create a child from the script manager?

Hello
I had quite a few doubts about how to create a cube but I don't know how to create a child for it, I will modify its values before creating it but I still have doubts about how to create a child for it.

import c4d
from c4d import documents
def main():
    def tool():
        return plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)
    c4d.CallCommand(5159) # Cubo

if __name__=='__main__':
    main()
    c4d.EventAdd()

Honestly, while CallCommand is fairly easy and the Script Log is logging you lots of CallCommands and some things are only possible through CallCommand...

...for things like object trees and other hierarchies, this is just the wrong way to go. CallCommand does not return the created object, and you would not have any variables to store references to other objects.

In case of creating child objects, BaseDocument's method InsertObject would be the way to go:

import c4d
from c4d import gui

def main():
    newCube = c4d.BaseObject(c4d.Ocube)
    doc.InsertObject(newCube)
    newChildCube = c4d.BaseObject(c4d.Ocube)
    doc.InsertObject(newChildCube, newCube)
    c4d.EventAdd()

if __name__=='__main__':
    main()

newCube and newChildCube are created here through the constructors of the BaseObject class, not by a CallCommand. In the second InsertObject call, the first cube is passed as the designated parent for the second cube.

(I would add a link to the BaseDocument documentation, but the server for the API docs seems to be down at the moment.)


(unavoidable advertising:)
Learn more about Python for C4D scripting:
https://www.patreon.com/cairyn

Thanks, but also my question would be how to do what I did with the original script, let me explain, using the callcomand I managed to edit the size and number of divisions, as well as make it editable, I don't know how I could achieve it with the script that helped me
my example:

import c4d
from c4d import gui
def main():
    def tool():
        return plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)
    def object():
        return doc.GetActiveObject()
    c4d.CallCommand(5159)
    object()[c4d.ID_BASELIST_NAME] = "cubo"
    object()[c4d.PRIM_CUBE_LEN,c4d.VECTOR_X] = 32
    object()[c4d.PRIM_CUBE_LEN,c4d.VECTOR_Y] = 32
    object()[c4d.PRIM_CUBE_LEN,c4d.VECTOR_Z] = 32
    object()[c4d.PRIM_CUBE_SUBX] = 11
    object()[c4d.PRIM_CUBE_SUBY] = 11
    object()[c4d.PRIM_CUBE_SUBZ] = 11
    c4d.CallCommand(12236)
if __name__=='__main__':
    main()

excuse my ignorance, I am new and I quite like the field of scripting

Well, you forgot to include that code in your original script, so I wouldn't know that this is the question 😀

Anyway, object() is just a function call that returns the currently active object, which happens to be the just created cube. In my code, the created code is stored in the variables newCube and newChildCube, respectively. So you can do the same modification of parameters by invoking it on the variables, once the object has been created:

newCube[c4d.ID_BASELIST_NAME] = "cubo"

etc.

Hello @JH23,

thank you for reaching out to us. I would second the advice from @Cairyn that using script log code is not the best idea (thank you for your help @Cairyn). Below you will a narrative code example which explains a few things which might be relevant for you when starting out with the Cinema 4D SDK.

Cheers,
Ferdinand

"""A script manger script that adds a cube with a cone object parented to it 
to the scene.

As discussed in:
    plugincafe.maxon.net/topic/13331
"""

import c4d


def main():
    # Assuming you want to use a CallCommand, you can do this. In practice
    # instantiating a cube object is better done in the way shown by Cairyn
    # in the forums.

    # The CallCommand for creating a cube object which will also insert and
    # select the cube.
    c4d.CallCommand(5159)

    # Get the currently active object; which is the new cube object due to the
    # prior CallCommand. The function object() created by the script log, and 
    # used in your code, does effectively something similar. But it has the 
    # overhead of retrieving the active object each time it is being called, 
    # which is not necessary here.
    
    # In a script environment there are also the predefined attributes op and
    # doc, "variables" in non-Python terms. op is the currently active object
    # when the script is being run and doc the currently active document.
    
    # We cannot use op here, since the CallCommand just did change that, but
    # we can use doc, which we could retrieve also manually if we had to via
    # c4d.documents.GetActiveDocument(). 
    cube = doc.GetActiveObject()

    # Set some of the parameters of that cube object.
    cube[c4d.ID_BASELIST_NAME] = "Cube"
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_X] = 32
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_Y] = 32
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_Z] = 32
    cube[c4d.PRIM_CUBE_SUBX] = 11
    cube[c4d.PRIM_CUBE_SUBY] = 11
    cube[c4d.PRIM_CUBE_SUBZ] = 11

    # Instantiate manually a cone object and insert it under that cube, i.e.,
    # do it like Cairyn has shown it.
    cone = c4d.BaseObject(c4d.Ocone)
    # We can also set the name of a node like this, which is a bit easier to
    # remember than the ID.
    cone.SetName("Cone")

    # Set some parameters of the cone. You can find out parameter ids via
    # drag and drop and the console, read more about it here:
    #   developers.maxon.net/docs/Cinema4DPythonSDK/html/misc/descriptions.html
    cone[c4d.PRIM_CONE_HEIGHT] = 32
    cone[c4d.PRIM_CONE_BRAD] = 16
    cone[c4d.ID_BASEOBJECT_REL_POSITION, c4d.VECTOR_Y] = 32

    # Create a phong tag for the cone, so that the cone is being shaded nicely.
    phongTag = cone.MakeTag(c4d.Tphong)
    # Enable the angle limit of the phong tag.
    phongTag[c4d.PHONGTAG_PHONG_ANGLELIMIT] = True

    # Insert the cone under the cube.
    cone.InsertUnder(cube)

    # When we are doing things "manually", i.e., do not use CallCommand, we
    # have to push an update to Cinema 4D after we have modified the scene
    # graph.
    c4d.EventAdd()


if __name__ == '__main__':
    main()

Thank you very much, they resolved my doubts 😄

Thank, but In the case that I wanted to make the obj editable, with callcomand I could just put the command to make editable but how would I do it in the case of the cube?

Hello @JH23,

first, I would like to point out that we ask the community here to post separate questions into separate postings, so that other users can find more easily answers when searching the forum. You can read more about the procedures of SDK support in our Forum Guidelines. It is okay in this case, but for future cases we have to ask you to open new topics for follow-up questions that are not directly related to the initial question of the topic (thread).

About your question - you have two options when you want to make an object editable:

  1. In case you already have the object in question already inserted into the active document and are in the main thread, you can still use CallCommand. For CallCommand you have to keep in mind that it usually relies on an object selection. When running it in a script, you have then make sure that actually the objects you want to be affected are selected when you do run the CallCommand. I have provided an extension of my prior example at the end of this posting.
  2. In all other cases, i.e., when the object is either in no document or not in the active one, or you are not on the main thread, then you must use c4d.utils.SendModelingCommand Link. In this case for example for the command MCOMMAND_MAKEEDITABLE. You can find more information about that in our SDK documentation and in the code examples on GitHub.

Cheers,
Ferdinand

Example code:

import c4d


def main():
    # Assuming you want to use a CallCommand, you can do this. In practice
    # instantiating a cube object is better done in the way shown by Cairyn
    # in the forums.

    # The CallCommand for creating a cube object which will also insert and
    # select the cube.
    c4d.CallCommand(5159)

    # Get the currently active object; which is the new cube object due to the
    # prior CallCommand. The function object() created by the script log, and 
    # used in your code, does effectively something similar. But it has the 
    # overhead of retrieving the active object each time it is being called, 
    # which is not necessary here.
    
    # In a script environment there are also the predefined attributes op and
    # doc, "variables" in non-Python terms. op is the currently active object
    # when the script is being run and doc the currently active document.
    
    # We cannot use op here, since the CallCommand just did change that, but
    # we can use doc, which we could retrieve also manually if we had to via
    # c4d.documents.GetActiveDocument(). 
    cube = doc.GetActiveObject()

    # Set some of the parameters of that cube object.
    cube[c4d.ID_BASELIST_NAME] = "Cube"
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_X] = 32
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_Y] = 32
    cube[c4d.PRIM_CUBE_LEN, c4d.VECTOR_Z] = 32
    cube[c4d.PRIM_CUBE_SUBX] = 11
    cube[c4d.PRIM_CUBE_SUBY] = 11
    cube[c4d.PRIM_CUBE_SUBZ] = 11

    # Instantiate manually a cone object and insert it under that cube, i.e.,
    # do it like Cairyn has shown it.
    cone = c4d.BaseObject(c4d.Ocone)
    # We can also set the name of a node like this, which is a bit easier to
    # remember than the ID.
    cone.SetName("Cone")

    # Set some parameters of the cone. You can find out parameter ids via
    # drag and drop and the console, read more about it here:
    #   developers.maxon.net/docs/Cinema4DPythonSDK/html/misc/descriptions.html
    cone[c4d.PRIM_CONE_HEIGHT] = 32
    cone[c4d.PRIM_CONE_BRAD] = 16
    cone[c4d.ID_BASEOBJECT_REL_POSITION, c4d.VECTOR_Y] = 32

    # Create a phong tag for the cone, so that the cone is being shaded nicely.
    phongTag = cone.MakeTag(c4d.Tphong)
    # Enable the angle limit of the phong tag.
    phongTag[c4d.PHONGTAG_PHONG_ANGLELIMIT] = True

    # Insert the cone under the cube.
    cone.InsertUnder(cube)
    
    # When we are doing things "manually", i.e., do not use CallCommand, we
    # have to push an update to Cinema 4D after we have modified the scene
    # graph.
    c4d.EventAdd()
    
    # Make the cube object the new active selection.
    doc.SetActiveObject(cube, c4d.SELECTION_NEW)
    # And run the " Make Editable" CallCommand on that
    c4d.CallCommand(12236) # Make Editable


if __name__ == '__main__':
    main()