Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    How to retrieve the text content (options/items) of a User Data Cycle Property?

    Cinema 4D SDK
    2025 python windows
    2
    3
    554
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • O
      Oliver
      last edited by Oliver

      Hello everyone,
      I'm working with Python in Cinema 4D and I've added a custom user data attribute to an object. This attribute is a Cycle (or enum) property, similar to Maya's enum, which generates a dropdown menu.
      My attribute has the following options:
      0;X
      1;Y
      2;Z
      3;-X
      ...
      I've written a script to switch this attribute's value programmatically. My current approach involves getting the selected object and accessing its user data. I can successfully change the value, but I'm relying on hardcoded IDs (e.g., op[descId] = 0 for 'X', op[descId] = 1 for 'Y', etc.).
      Here's a snippet of my current code:

      import c4d
      
      ATTR_NAME = 'Axis'
      
      userData = op.GetUserDataContainer()
      
      for descId, container in userData:
          if container.GetInt32(c4d.DESC_CUSTOMGUI) != c4d.CUSTOMGUI_CYCLE:
              continue
          if container[c4d.DESC_NAME] != ATTR_NAME:
              continue
          
          enumString = container.GetString(c4d.CUSTOMGUI_CYCLE)
          print(enumString) # is None
          
          # clicked X
          op[descId] = 0
      
      c4d.EventAdd()
      

      My goal is to be able to switch the attribute's value by its name (e.g., 'X', '-Z') instead of its integer ID. To do this, I need to retrieve the full list of options (the 0;X\n1;Y... string) from the Cycle property itself, so I can map the option names to their corresponding integer IDs.
      I tried using container.GetString(c4d.DESC_CYCLE), but it returned an empty string, which is unexpected for a properly set up Cycle user data.
      Could anyone guide me on the correct way to extract this textual content (the 0;X\n1;Y... string) from a User Data Cycle property in Python?
      Any help would be greatly appreciated!
      4a726e5a95fc06abf24b2f56467cb81.png

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @Oliver
        last edited by ferdinand

        Hey @Oliver,

        Thank you for reaching out to us. You can technically achieve that by accessing the description, but that is not really how parameter access is intended in Cinema 4D. We do not use strings but symbols to access things. And when you want an X, Y, Z symbol, you can just define it.

        In plugins this is usually done in either a description, dialog, or global resource. When you just have some user data and some local script, you can just do it in that file.

        Cheers,
        Ferdinand

        Result

        e47256f0-8bb2-48cc-a143-367d75dbc347-image.png
        File: param_access.c4d

        Code

        import c4d
        import typing
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        def main() -> None:
            """Called by Cinema 4D when the script is being executed.
            """
            if not op:
                c4d.gui.MessageDialog("No object selected.")
                return
            
            # Cinema 4D operates with something called "symbols", i.e., identifiers defined in code, to 
            # address parameters in the Cinema API. In the Maxon API we also use string identifiers, but
            # that is another subject.
        
            # So, to access the position of an object, we must do this:
            print("Position: ", op[903])
        
            # Because the integer value 903 is where this data is being stored in the object. But that is
            # not very readable, so we can use the symbol `c4d.ID_BASEOBJECT_POSITION:
            print("Position: ", op[c4d.ID_BASEOBJECT_POSITION])
        
            # This applies to many many things in Cinema 4D, that they are identified by integer values,
            # which then usually are expressed as symbols in code.
        
            # User data, the case you are talking about, by definition cannot have symbols, because the user
            # just defined that parameter. E.g., we access (ID_USERDATA, 1) and not (ID_USERDATA, SOME_SYMBOL).
            try:
                value: typing.Any = op[c4d.ID_USERDATA, 1]  # Accessing user data with ID 1.
            except:
                c4d.gui.MessageDialog("User data with ID 1 does not exist.")
                return
            
            # When we now assume that #value is of type `int` ...
            value: int
        
            # ... then there exist of course no builtin symbols for it, but we can make some up.
            ID_X_AXIS: int = 0
            ID_Y_AXIS: int = 1
            ID_Z_AXIS: int = 2
        
            if value == ID_X_AXIS:
                print("User data is set to X axis.")
        
            op[c4d.ID_USERDATA, 1] = ID_Y_AXIS  # Set user data to Y axis.
        
            # Lastly, for some things exists symbols you can simply reuse. You could for example hijack
            # the Mograph control for this as defined in [1], so that you would write:
            #
            # [1] https://developers.maxon.net/docs/py/2025_2_0/cinema_resource/object/mggridarray.html
            if op[c4d.ID_USERDATA, 1] == c4d.MG_GRID_UVAXIS_XP:
                print("User data is set to +X axis.")
        
            # Which should be complemented by going into for example the Mograph Cloner, navigating to the
            # W(UV)-Orientation parameter in the Transform tab, right click it, and then select 
            # "User Data Interface > Copy User Data Interface". To then go into our object, and then select
            # from the Attribute Manager "User Data > Paste User Data Interface ...". You can not just copy
            # singular parameters like this but whole interfaces. Each user data parameter is then still
            # editable in the User Data Manager, so you can change the names and such.
        
            # When you are hell-bent on doing it, you can also evaluate label-id relations at runtime, but
            # that is not how this is done in Cinema 4D.
        
            description: c4d.Description = op.GetDescription(c4d.DESCFLAGS_DESC_0)
            data: c4d.BaseContainer = description.GetParameter(
                c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)))
            
            print (f"{data[c4d.DESC_NAME] = } with the cycle values:")
            for value, label in data[c4d.DESC_CYCLE]:
                print(f"{label = }, {value = }")
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        O 1 Reply Last reply Reply Quote 1
        • O
          Oliver @ferdinand
          last edited by

          @ferdinand
          Thank you for the detailed explanation and the solution! This perfectly solved my problem, and I really appreciate you clarifying how to access the DESC_CYCLE values!!

          1 Reply Last reply Reply Quote 0
          • First post
            Last post