Solved Miscellaneous questions about "BaseContainer","DescID" etc

[I have so many questions in this thread. So first, please let me thank you for taking time! :relaxed:]

I want to access user data in a more "procedural way". Here is the code after I do lots of searching on the Python (and C++) SDK document:

import c4d
from c4d import gui
#Welcome to the world of Python

def main():
    obj = doc.GetActiveObject()
    objBc = obj.GetData()
    userdataList = obj.GetUserDataContainer()

    userdata = userdataList[0]
    userdataBc = userdata[1]
    DescIDList = userdata[0]
    id0 = DescIDList[0].id
    id1 = DescIDList[1].id

if __name__=='__main__':

After this, I really want to know how the data of a BaseObject organized and stored. And I make a picture:


I don't know if this reveals some of the data structure of BaseObject.

And I have some doubts in or out of the picture:

  1. [In the picture] After I search in the BaseContainer of BaseObject, I don't find anything data related to a Userdata: long name, short name, min/max value/default value, etc.
    Where is the BaseContainer of all these data stored?

  2. [In the picture] Is DescID is similar to UUID of a BaseContainer? And where are they stored?

  3. [In the picture] What is CreatorId of DescLevel? I only find 0 in this field.

  4. [Out the picture] As for now, is there any easier way to get a textual form of and DescLevel.dtype? Comparing and searching in branches of *.h files is not friendly for us without C++ experience.

  5. [Off topic] The name index in sample code of BaseContainer.__iter__() in Python SDK Document is confusing. Especially that all other function documents refer the same thing as id on the same page.

  6. [Off topic] Is there any articles that have more detail about the foundation concepts for cinema 4d coding?
    Here I find some pages in Python SDK Document:
    Cookbook, Plugin Structure, Introduction into the GUI of Cinema 4D.
    But they are lack of details and relation with other document.
    Also, some foundation concepts only have few introduction paragraphs:
    BaseList2D,BaseContainer, etc.
    I don't mean to complain, but it's frustrating after spending hours and hours searching in the SDK and the Internet but only get some obscure results. I hope the document could be more friendly to new developers.

Thanks a lot!


An object like BaseObject can store data. This data is typically stored in the object's BaseContainer. But there are cases where the data is not stored in the BaseContainer so in general it is saver to use GetParameter().

A BaseContainer can store any kind of data. This includes other BaseContainers. The BaseContainer of the object stores a sub-BaseContainer that stores all the values of the user data. The ID of the User-Data-BaseContainer is c4d.ID_USERDATA. Using this ID you can obtain the BaseContainer:

bc = op.GetData()
userDataValues = bc[c4d.ID_USERDATA]

# or

userDataValues = op.GetParameter(c4d.ID_USERDATA, c4d.DESCFLAGS_GET_0)

This BaseContainer stores the parameter values of user data parameters.

A parameter is identified using a DescID. This DescID has nothing to do with UUID.

A DescID is composed of one or several DescLevel components. In most cases the DescID has only one DescLevel. The DescLevel defines these properties:

  • id: id of the level. Typically the parameter ID.
  • dtype: type of the parameter.
  • creator: ID of the "creator" of the parameter. E.g. on a cube you have some parameters that were created by the "Cube" (Size, Segments, ...). But there are also other parameters that are shared by all BaseObject's (Position, Scale, ...). These shared parameters are inherited from a base class, in this case Obase (5155). The "creator" value is not always relevant so it might not always be set.

A DescID can have multiple DescLevels since a parameter may be stored inside another parameter. As described above, user data values are stored in a BaseContainer that is stored in the default BaseContainer. The full DescID of a user-data parameter has to reflect that. The DescID of a user-data parameter must first identify the user-data container and then the actual parameter in that container. This is done using two DescLevels:

id = c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 0), c4d.DescLevel(1))

value = op.GetParameter(id, c4d.DESCFLAGS_GET_0)

But the Python API also allows you to do the same with less code:

value = op[c4d.ID_USERDATA, 1]

Or you access the user-data BaseContainer:

bc = op.GetData()
userDataValues = bc[c4d.ID_USERDATA]
value = userData[1]

The DescIDs are not necessarily stored in a file. The number of parameters of an object may change depending on how the object is used. The list of all displayed parameters is stored in the parameter Description. This Description contains the IDs as well as information on how to display the parameters in the Attribute Manager. The information how the parameter should be displayed is also stored as a BaseContainer (but this is NOT the object's BaseContainer).

The parameter description of user data parameters is stored in the object. You can access this data with GetUserDataContainer():

for id, descriptionBC in op.GetUserDataContainer():
	print("User Data ID: " + str(id))
	print("User Data Value: " + str(op[id]))
	print("User Data Name: " + descriptionBC[c4d.DESC_NAME])

To make that clear: the VALUES of user data parameters are stored in the object's BaseContainer. The DESCRIPTION of the user data parameters is stored in the BaseContainers accessed with GetUserDataContainer().

You find an overview over the most fundamental concepts and classes currently in the C++ documentation under Foundations, especially Classic API Base Classes.

You can also find further information about these classes:

best wishes,

@s_bach, thank you, I'll start to do some hard reading!