_c4d_basecontainer

Demonstrates the usage of the fundamental container type in the Cinema API.

_c4d_basecontainer.py

"""Demonstrates the usage of the fundamental container type in the Cinema API.

Run this example in the Cinema 4D Script Manager.

Topics:
    - Copying containers
    - Iterating over containers
    - Nested containers
    - Container IDs
    - Using containers in message and callback contexts
"""
__author__ = "Ferdinand Hoppe"
__copyright__ = "Copyright (C) 2026 MAXON Computer GmbH"
__license__ = "Apache-2.0 License"
__version__ = "2027"

import c4d
import mxutils

doc: c4d.documents.BaseDocument # The active document.


def main() -> None:
    """Demonstrates the usage of the fundamental container type in the Cinema API.
    """
    # BaseContainer is the type which holds most bundles of scene data. Each BaseList2D, i.e.,
    # scene element, has a container which holds most, but not all, of its data. There are two
    # ways to get the data container of a scene element.

    # Create a new scene element, a cube.
    cube: c4d.BaseObject = mxutils.CheckType(c4d.BaseObject(c4d.Ocube))
    doc.InsertObject(cube)

    # BaseList2D.GetData() returns a COPY of the data container, so that modifications to the 
    # returned container do not affect the scene element. It is not recommend to use GetData() 
    # for modifying scene elements, as reading and writing the full container is quite expensive.
    dataCopy: c4d.BaseContainer = cube.GetData()

    # BaseList2D.GetContainerInstance() returns a reference to the data container, so that
    # modifications to the returned container do affect the scene element.
    dataRef: c4d.BaseContainer = cube.GetDataInstance()

    # Set the size of the cube by writing to its data container.
    dataRef[c4d.PRIM_CUBE_LEN] = c4d.Vector(200.0, 200.0, 200.0)

    # We can also iterate over the container, which yields key-value pairs. Note that symbols, e.g.,
    # c4d.PRIM_CUBE_LEN, are just integer constants, so the keys are integers. 
    for key, value in dataRef:
        print(f"Key: {key}, Value: {value}")

    # We can also construct containers ourself and containers can be nested.
    myContainer: c4d.BaseContainer = c4d.BaseContainer()
    myContainer[1000] = "Hello World"
    myContainer[1001] = 42
    subContainer: c4d.BaseContainer = c4d.BaseContainer()
    subContainer[2000] = c4d.Vector(1.0, 2.0, 3.0)
    myContainer[1002] = subContainer

    # There are also specific functions to get and set values of a specific type, but we usually do 
    # not need them in Python, there are just a reflection of the C++ API.
    subContainer.SetInt32(2001, 42)
    value: int = subContainer.GetInt32(2001)

    # Besides atomic types such as int, float, bool, str, and vectors, BaseContainer can also hold 
    # more complex types. In Python we do not have all the options of the C++ API, but we can for 
    # example store a BaseLink or BaseTime in a container.
    subContainer[2002] = c4d.BaseTime(1.0)
    subContainer[2003] = cube

    # Now we can retrieve the values we stored in the container. Note that we stored a LINK to the 
    # cube in the container, not the object itself. For retrieving the linked object, it can be 
    # useful to use the dedicated GetLink() function, as we can specify there in which document we 
    # want to look for the linked object.
    time: c4d.BaseTime = subContainer[2002]
    linkedObject: c4d.BaseObject = subContainer[2003]

    # Container can be copied with GetClone().
    containerCopy: c4d.BaseContainer = myContainer.GetClone(c4d.COPYFLAGS_NONE)
    # And we can copy one container into another with CopyTo().
    containerCopy.CopyTo(myContainer, c4d.COPYFLAGS_NONE)
    # And we can flush a container with FlushAll(), which removes all key-value pairs.
    containerCopy.FlushAll()

    # Containers can have an ID. By default it is 0, but in a message context, this is sometimes set 
    # to the message ID. We can also specify an ID when constructing a container.
    myContainerWithID: c4d.BaseContainer = c4d.BaseContainer(42)
    myContainerWithID.SetId(24)
    cid: int = myContainerWithID.GetId()

    # Finally, we often encounter containers in message and callback contexts. Here we get for 
    # example the state of the mouse.
    state: c4d.BaseContainer = c4d.BaseContainer()
    c4d.gui.GetInputState(c4d.BFM_INPUT_MOUSE, 0, state)

    # A useful tool when dealing with containers can be the function GetContainerTreeString(), which 
    # returns a string representation of the container and its contents. It also has builtin symbol 
    # translation for the Cinema 4D symbols, so that we can see the symbol names instead of just the 
    # integer values. See the documentation of GetContainerTreeString() for more information.
    print(f"\nMouse Input State:\n\n{mxutils.GetContainerTreeString(state, '^BFM_')}")

    c4d.EventAdd()


if __name__ == "__main__":
    main()