Python Cloner Effectors Keyframe

Hi,

I'm trying to add a keyframe of the strength of the inheritance that it is inside of a cloner effectors. (See image)
Cinema4D-Question.PNG

In order to add keyframes I know that I need to have a CTrack however I'm unable to locate which DescID or more specifically which DescLevel I need to use to achieve my goal.

How can I find the DescLevel I need?

Thank you for your time.

Joel.

@joel
The simplest way is to drag the parameter to the python console, then you could get desclevel id without dtype and creator. e.g.c4d.DescID(1500).
1.gif
If you want to get the DescID programmatically. I wrote a function to list all parameters and detail descriptions of an object. This may help you.

import c4d

def find_ident(value: int, prefix=""):
    for key, val in c4d.__dict__.items():
        if val == value and key.startswith(prefix):
            return f"c4d.{key}"

def dump_params(obj: c4d.BaseList2D):
    def dump_param_desc(bc):
        for k, v in bc:
            ident = find_ident(k, "DESC_")
            if type(v) == str:
                v = repr(v)
            if not ident:
                continue
            if k == c4d.DESC_UNIT:
                v = find_ident(v, "DESC_")
            if k == c4d.DESC_CUSTOMGUI:
                v = find_ident(v, "CUSTOMGUI") or v
            if isinstance(v, c4d.Vector) and abs(v.x) > 1e10:
                v = "Vector"
            print('\t', ident, ":", v)

    description = obj.GetDescription(c4d.DESCFLAGS_DESC_0)
    descid: c4d.DescID
    for bc, descid, _ in description:
        name = bc[c4d.DESC_NAME] or bc[c4d.DESC_SHORT_NAME]
        if not name:
            continue
        if descid[descid.GetDepth() - 1].dtype == c4d.DTYPE_GROUP:
            print("---------")
        print(name, descid, ":", repr(obj[descid]))
        dump_param_desc(bc)
        if bc[c4d.DESC_CUSTOMGUI] == c4d.CUSTOMGUI_SUBDESCRIPTION:
            for subvector in [c4d.VECTOR_X, c4d.VECTOR_Y, c4d.VECTOR_Z]:
                subdescid = c4d.DescID(*[descid[i] for i in range(descid.GetDepth())])
                subdescid.PushId(c4d.DescLevel(subvector))
                subbc = description.GetParameter(subdescid)
                name = subbc[c4d.DESC_NAME] or subbc[c4d.DESC_SHORT_NAME]
                if not name:
                    continue
                name = name.replace(" . ", ".")
                print(name, ":", obj[subdescid])
                dump_param_desc(subbc)

if __name__ == "__main__":
    obj = doc.GetActiveObject()
    dump_params(obj)

Following is the outputs to the end:

Effectors (2009, 1009290, 1018639) : <c4d.InExcludeData object at 0x00000198DD88E9C0>
	 c4d.DESC_NAME : 'Effectors'
	 c4d.DESC_SHORT_NAME : 'Effectors'
	 c4d.DESC_VERSION : 3
	 c4d.DESC_ANIMATE : 0
	 c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_INEXCLUDE_LIST
	 c4d.DESC_ACCEPT : <c4d.BaseContainer object at 0x00000198DD88DE00>
	 c4d.DESC_IDENT : 'ID_MG_MOTIONGENERATOR_EFFECTORLIST'
Plain (1500, 19, 1018544) : 0.99
	 c4d.DESC_NAME : 'Plain'
	 c4d.DESC_SHORT_NAME : 'Plain'
	 c4d.DESC_VERSION : 3
	 c4d.DESC_MIN : -1e+20
	 c4d.DESC_MAX : 1e+20
	 c4d.DESC_MINEX : 0
	 c4d.DESC_MAXEX : 0
	 c4d.DESC_STEP : 0.01
	 c4d.DESC_ANIMATE : 1
	 c4d.DESC_UNIT : c4d.DESC_UNIT_PERCENT
	 c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_REALSLIDER
	 c4d.DESC_MINSLIDER : 0.0
	 c4d.DESC_MAXSLIDER : 1.0
	 c4d.DESC_REMOVEABLE : 0
Plain.1 (1501, 19, 1018544) : 1.0
	 c4d.DESC_NAME : 'Plain.1'
	 c4d.DESC_SHORT_NAME : 'Plain.1'
	 c4d.DESC_VERSION : 3
	 c4d.DESC_MIN : -1e+20
	 c4d.DESC_MAX : 1e+20
	 c4d.DESC_MINEX : 0
	 c4d.DESC_MAXEX : 0
	 c4d.DESC_STEP : 0.01
	 c4d.DESC_ANIMATE : 1
	 c4d.DESC_UNIT : c4d.DESC_UNIT_PERCENT
	 c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_REALSLIDER
	 c4d.DESC_MINSLIDER : 0.0
	 c4d.DESC_MAXSLIDER : 1.0
	 c4d.DESC_REMOVEABLE : 0

Then you can get the DescID: c4d.DescID(c4d.DescLevel(1500, 19, 1018544))

I will test it out. Thank you so much.

Hello @joel,

Thank you for reaching out to us and thanks again @iplai for the detailed answer.

Here also not much to be added from our side, everything @iplai said was correct. What should be added though IMHO is that what you are dealing here with, is a Description which is modified at runtime. E.g., when you have the Cube Object, its description is fully static, all its parameters are statically defined in its resource (ocube.res and the matching ocube.h file). You can therefore predict all its IDs and we can provide symbols for them, e.g., PRIM_CUBE_LEN for the size of the cube.

The Cloner Object description is however not fully static. The sliders below the Effectors list depend on the (number of) objects the user has dragged into the list. It is not possible to know ahead of time which symbols are there.

You can then solve this in two ways:

  1. Figure out the stride/offset of things: In this case it is easy, the sliders are placed from the ID 1500 on with a stride of 1, e.g., [1500, 1501, 1502, ...]. You can then simply test out with C4DAtom.GetParamaeter up to which point the sliders exist by trying to retrieve their value.
  2. Inspect the description of the node as @iplai did. This is a more sophisticated approach, but be warned, in some rare cases where our developers went haywire with modifying the description at runtime, C4DAtom.GetDescription() might not always return the true state of the dynamically modified description. Which then results in parameters which are shown in the interface not to be contained in the description copy returned by C4DAtom.GetDescription().

Cheers,
Ferdinand

@ferdinand
Hi again,
I am bumping into an issue that I do not know up to which point is a bug.
All that was described before works without issues. The problems arise when the inheritance gets deleted by the user via the GUI. In the GUI the Effectors list is empty but in python code, it says that the ObjectCount of the Effectors list is not empty.Question.PNG
Question2.PNG

How can this be?

PD: This is my second post on this forum. For this type of issues is it better to open another Topic?