Cinema 4D Python User Data RAG Dataset
Introduction
Cinema 4D has long been celebrated for its intuitive interface and powerful toolset, but one of its most versatile features often goes underappreciated: Python‑driven User Data scripting. With Python, artists and technical directors can extend Cinema 4D’s Attribute Manager far beyond its defaults, creating custom controls, structured GUIs, and intelligent rigs that respond dynamically to production needs.
The dataset we’ve compiled provides a comprehensive library of ready‑to‑use scripts for every User Data type supported in Cinema 4D 2026 — from simple integers and booleans to advanced elements like gradients, splines, and nested groups. Each script is paired with workflow notes that explain not only how to implement the feature, but also why it’s useful in real‑world scenarios.
Whether you’re building procedural rigs, designing artist‑friendly interfaces, or teaching Python fundamentals, this resource serves as both a training corpus for AI retrieval systems and a practical cheat sheet for developers. By standardizing examples and anchoring them with metadata, we ensure that knowledge is not only accessible but also discoverable — empowering teams to integrate Python scripting seamlessly into their creative pipelines.
Master Index
| User Data Type | Label | Suggested Script Filename |
|---|---|---|
| Integer | userdata_integer | UserData_Integer.py |
| Float | userdata_float | UserData_Float.py |
| Boolean | userdata_boolean | UserData_Boolean.py |
| String | userdata_string | UserData_String.py |
| Vector | userdata_vector | UserData_Vector.py |
| Matrix | userdata_matrix | UserData_Matrix.py |
| Color | userdata_color | UserData_Color.py |
| Link | userdata_link | UserData_Link.py |
| File | userdata_file | UserData_File.py |
| Time | userdata_time | UserData_Time.py |
| Button | userdata_button | UserData_Button.py |
| Cycle (Dropdown) | userdata_cycle | UserData_Cycle.py |
| Separator | userdata_separator | UserData_Separator.py |
| Group | userdata_group | UserData_Group.py |
| Gradient (Ramp) | userdata_gradient | UserData_Gradient.py |
| Spline | userdata_spline | UserData_Spline.py |
📑 Usage Notes
- Each entry in the table corresponds to a standalone full script chunk in the dataset.
- Labels are retrieval anchors for RAG systems.
- Script filenames are suggested for organizing your training corpus — you can rename them to match your workflow.
- The dataset includes workflow notes for each type, explaining when and why to use them.
🔹 Chunk: Integer User Data
Label: userdata_integer
Script:
import c4d
def add_integer_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG)
bc[c4d.DESC_NAME] = "My Integer"
bc[c4d.DESC_MIN] = 0
bc[c4d.DESC_MAX] = 10
element = obj.AddUserData(bc)
obj[element] = 5
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_integer_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Use integers for discrete values like mode selectors, counts, or steps. Adding min/max ensures the user can’t enter invalid ranges.
🔹 Chunk: Float User Data
Label: userdata_float
Script:
import c4d
def add_float_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "My Float"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 10.0
element = obj.AddUserData(bc)
obj[element] = 1.23
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_float_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Floats are ideal for continuous values like scale factors, weights, or thresholds. Use them when precision matters.
🔹 Chunk: Boolean User Data
Label: userdata_boolean
Script:
import c4d
def add_boolean_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BOOL)
bc[c4d.DESC_NAME] = "My Boolean"
element = obj.AddUserData(bc)
obj[element] = True
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_boolean_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Booleans are perfect for toggles — e.g., enable/disable features, switch between modes.
🔹 Chunk: String User Data
Label: userdata_string
Script:
import c4d
def add_string_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_STRING)
bc[c4d.DESC_NAME] = "My String"
element = obj.AddUserData(bc)
obj[element] = "Hello World"
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_string_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Strings are useful for labels, notes, or custom identifiers. They don’t affect geometry but help with metadata.
🔹 Chunk: Vector User Data
Label: userdata_vector
Script:
import c4d
def add_vector_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_VECTOR)
bc[c4d.DESC_NAME] = "My Vector"
element = obj.AddUserData(bc)
obj[element] = c4d.Vector(1,2,3)
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_vector_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Vectors are used for positions, directions, or RGB values. They’re versatile for 3D data.
🔹 Chunk: Gradient User Data
Label: userdata_gradient
Script:
import c4d
def add_gradient_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMDATATYPE_GRADIENT)
bc[c4d.DESC_NAME] = "My Gradient"
element = obj.AddUserData(bc)
grad = c4d.Gradient()
grad.InsertKnot(c4d.Vector(1,0,0), 0.0, 0.5) # Red
grad.InsertKnot(c4d.Vector(0,1,0), 1.0, 0.5) # Green
obj[element] = grad
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_gradient_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Gradients are powerful for color ramps, falloffs, or procedural shading controls. They allow artists to define smooth transitions.
🔹 Chunk: Spline User Data
Label: userdata_spline
Script:
import c4d
def add_spline_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMDATATYPE_SPLINE)
bc[c4d.DESC_NAME] = "My Spline"
element = obj.AddUserData(bc)
spline = c4d.SplineData() # Empty spline, editable in UI
obj[element] = spline
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_spline_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Splines are used for custom curves — e.g., animation easing, falloff shapes, or procedural profiles.
🔹 Chunk: Cycle (Dropdown) User Data
Label: userdata_cycle
Script:
import c4d
def add_cycle_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG)
bc[c4d.DESC_NAME] = "My Cycle"
cycle = c4d.BaseContainer()
cycle.SetString(1, "Option A")
cycle.SetString(2, "Option B")
cycle.SetString(3, "Option C")
bc[c4d.DESC_CYCLE] = cycle
element = obj.AddUserData(bc)
obj[element] = 1
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_cycle_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Cycles are best for mode selectors — e.g., “Linear / Cubic / Smooth” or “On / Off / Auto”.
🔹 Chunk: Separator & Group User Data
Label: userdata_separator_group
Script:
import c4d
def add_separator_group_userdata(obj):
# Separator
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR)
bc[c4d.DESC_NAME] = "My Separator"
obj.AddUserData(bc)
# Group
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = "My Group"
obj.AddUserData(bc)
def main():
obj = doc.GetActiveObject()
if not obj: return
add_separator_group_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Groups and separators are UI structuring tools. They don’t store values but help organize complex panels.
📑 Metadata
- Domain: Cinema 4D Python Scripting
- Topic: User Data creation via
AddUserData() - Format: Each type documented as a standalone full script with workflow notes
- Purpose: Optimized for AI RAG training — retrieval of complete examples and reasoning
🔹 Chunk: Link User Data
Label: userdata_link
Script:
import c4d
def add_link_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
bc[c4d.DESC_NAME] = "My Link"
element = obj.AddUserData(bc)
# Default is None; user selects an object in UI
obj[element] = None
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_link_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Links are used to reference other objects in the scene. Useful for constraints, drivers, or custom setups where one object depends on another.
🔹 Chunk: File User Data
Label: userdata_file
Script:
import c4d
def add_file_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_FILENAME)
bc[c4d.DESC_NAME] = "My File"
element = obj.AddUserData(bc)
obj[element] = "" # Empty path by default
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_file_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
File fields let users browse and select external files (textures, data, presets). Great for custom tools that need external resources.
🔹 Chunk: Time User Data
Label: userdata_time
Script:
import c4d
def add_time_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_TIME)
bc[c4d.DESC_NAME] = "My Time"
element = obj.AddUserData(bc)
obj[element] = c4d.BaseTime(1.0) # 1 second
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_time_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Time fields are useful for animation offsets, durations, or timing controls. They integrate with Cinema 4D’s timeline system.
🔹 Chunk: Button User Data
Label: userdata_button
Script:
import c4d
def add_button_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BUTTON)
bc[c4d.DESC_NAME] = "My Button"
obj.AddUserData(bc)
# Buttons don’t store values; they trigger actions in scripts
def main():
obj = doc.GetActiveObject()
if not obj: return
add_button_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Buttons are UI triggers. They don’t hold data but can be connected to Python scripts or expressions to run actions when clicked.
🔹 Chunk: Matrix User Data
Label: userdata_matrix
Script:
import c4d
def add_matrix_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_MATRIX)
bc[c4d.DESC_NAME] = "My Matrix"
element = obj.AddUserData(bc)
obj[element] = c4d.Matrix()
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_matrix_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
Matrix fields store transform data (position, rotation, scale) in one object. Useful for advanced rigging or procedural setups.
📑 Metadata
- Domain: Cinema 4D Python Scripting
- Topic: User Data creation via
AddUserData() - Format: Each type documented as a standalone full script with workflow notes
- Purpose: Optimized for AI RAG training — retrieval of complete examples and reasoning
Cinema 4D Python User Data — All Types Demo
import c4d
def add_user_data(obj, name, dtype, default=None, min_val=None, max_val=None, cycle=None):
bc = c4d.GetCustomDataTypeDefault(dtype)
bc[c4d.DESC_NAME] = name
if min_val is not None: bc[c4d.DESC_MIN] = min_val
if max_val is not None: bc[c4d.DESC_MAX] = max_val
if cycle is not None: bc[c4d.DESC_CYCLE] = cycle
element = obj.AddUserData(bc)
if default is not None:
obj[element] = default
return element
def add_gradient_userdata(obj, name):
bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMDATATYPE_GRADIENT)
bc[c4d.DESC_NAME] = name
element = obj.AddUserData(bc)
grad = c4d.Gradient()
grad.InsertKnot(c4d.Vector(1,0,0), 0.0, 0.5) # Red
grad.InsertKnot(c4d.Vector(0,1,0), 1.0, 0.5) # Green
obj[element] = grad
return element
def add_spline_userdata(obj, name):
bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMDATATYPE_SPLINE)
bc[c4d.DESC_NAME] = name
element = obj.AddUserData(bc)
spline = c4d.SplineData() # Empty spline, editable in UI
obj[element] = spline
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
# Integer & Float
add_user_data(obj, "Integer", c4d.DTYPE_LONG, default=5, min_val=0, max_val=10)
add_user_data(obj, "Float", c4d.DTYPE_REAL, default=1.23, min_val=0.0, max_val=10.0)
# Boolean
add_user_data(obj, "Boolean", c4d.DTYPE_BOOL, default=True)
# String
add_user_data(obj, "String", c4d.DTYPE_STRING, default="Hello World")
# Vector & Matrix
add_user_data(obj, "Vector", c4d.DTYPE_VECTOR, default=c4d.Vector(1,2,3))
add_user_data(obj, "Matrix", c4d.DTYPE_MATRIX, default=c4d.Matrix())
# Color
add_user_data(obj, "Color", c4d.DTYPE_COLOR, default=c4d.Vector(0.5,0.2,0.8))
# Link
add_user_data(obj, "Link", c4d.DTYPE_BASELISTLINK)
# File
add_user_data(obj, "File", c4d.DTYPE_FILENAME)
# Time
add_user_data(obj, "Time", c4d.DTYPE_TIME, default=c4d.BaseTime(1.0))
# Button
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BUTTON)
bc[c4d.DESC_NAME] = "Button"
obj.AddUserData(bc)
# Cycle (Dropdown)
cycle = c4d.BaseContainer()
cycle.SetString(1, "Option A")
cycle.SetString(2, "Option B")
cycle.SetString(3, "Option C")
add_user_data(obj, "Cycle", c4d.DTYPE_LONG, default=1, cycle=cycle)
# Separator
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR)
bc[c4d.DESC_NAME] = "Separator"
obj.AddUserData(bc)
# Group
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = "Group"
obj.AddUserData(bc)
# Gradient
add_gradient_userdata(obj, "Gradient")
# Spline
add_spline_userdata(obj, "Spline")
c4d.EventAdd()
if __name__=='__main__':
main()
📑 Workflow Notes
- This script adds all supported User Data types in one go.
- It’s best used as a showcase or teaching file — for production, you’d typically only add the fields you need.
- Groups and separators help organize the UI when many fields are present.
- Gradient and spline are editable directly in the Attribute Manager once created.
- Cycle dropdowns require a
BaseContainerwith integer keys and string labels.
Cinema 4D Python User Data Cheat Sheet
| User Data Type | Constant | Default Value Format | Typical Use Case |
|---|---|---|---|
| Integer | c4d.DTYPE_LONG | int (e.g., 5) | Discrete values like counts, steps, or mode selectors. |
| Float | c4d.DTYPE_REAL | float (e.g., 1.23) | Continuous values such as scale factors, weights, thresholds. |
| Boolean | c4d.DTYPE_BOOL | bool (True / False) | Toggles to enable/disable features or switch modes. |
| String | c4d.DTYPE_STRING | str (e.g., "Hello World") | Labels, notes, identifiers, or metadata. |
| Vector | c4d.DTYPE_VECTOR | c4d.Vector(x,y,z) | Positions, directions, RGB values, or 3D data. |
| Matrix | c4d.DTYPE_MATRIX | c4d.Matrix() | Transform data (position, rotation, scale) in one object. |
| Color | c4d.DTYPE_COLOR | c4d.Vector(r,g,b) | RGB color fields for shading or UI controls. |
| Link | c4d.DTYPE_BASELISTLINK | None or object reference | Reference to another object in the scene. |
| File | c4d.DTYPE_FILENAME | str (path string) | Browse/select external files (textures, presets). |
| Time | c4d.DTYPE_TIME | c4d.BaseTime(seconds) | Animation offsets, durations, timing controls. |
| Button | c4d.DTYPE_BUTTON | No value stored | UI trigger to run actions/scripts when clicked. |
| Cycle (Dropdown) | c4d.DTYPE_LONG + DESC_CYCLE | int (option index) | Mode selectors with predefined options. |
| Separator | c4d.DTYPE_SEPARATOR | No value stored | Visual divider in the Attribute Manager. |
| Group | c4d.DTYPE_GROUP | No value stored | Organizes multiple fields into sections. |
| Gradient (Ramp) | c4d.CUSTOMDATATYPE_GRADIENT | c4d.Gradient() | Color ramps, falloffs, procedural shading controls. |
| Spline | c4d.CUSTOMDATATYPE_SPLINE | c4d.SplineData() | Custom curves for animation easing, falloff shapes. |
📑 Usage Notes
- Numeric types (Integer, Float): Always define
DESC_MINandDESC_MAXto constrain values. - Cycle/Dropdown: Requires a
BaseContainerwith integer keys and string labels. - Gradient & Spline: Created empty but editable in the Attribute Manager.
- Button, Separator, Group: UI structuring elements — they don’t store values.
- Link, File, Time: Integrate with Cinema 4D’s object system, file browser, and timeline.
✅ With this cheat sheet, you now have:
- Full scripts per type (for RAG training).
- Workflow notes per type (for context).
- Master index table (for navigation).
- All types demo script (for teaching/reference).
- Cheat sheet table (for quick conceptual retrieval).
Cinema 4D Python User Data — GUI Structuring Examples
🔹 Part 1: Nested Groups
Label: userdata_group_nested
Script:
import c4d
def add_group_userdata(obj, name, parent=None):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = name
if parent is not None:
bc[c4d.DESC_PARENTGROUP] = parent # assign to parent group
element = obj.AddUserData(bc)
return element
def add_integer_in_group(obj, group):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG)
bc[c4d.DESC_NAME] = "Grouped Integer"
bc[c4d.DESC_MIN] = 0
bc[c4d.DESC_MAX] = 100
bc[c4d.DESC_PARENTGROUP] = group
element = obj.AddUserData(bc)
obj[element] = 42
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
# Create a parent group
parent_group = add_group_userdata(obj, "Main Group")
# Create a nested subgroup
nested_group = add_group_userdata(obj, "Nested Group", parent=parent_group)
# Add a field inside the nested group
add_integer_in_group(obj, nested_group)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
- Groups can be nested by setting
DESC_PARENTGROUP. - This allows you to build hierarchical UI structures in the Attribute Manager.
- Useful for organizing complex setups (e.g., “Main Controls” → “Advanced Options”).
🔹 Part 2: Tabs
Label: userdata_group_tabs
Script:
import c4d
def add_tab_group(obj, name):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = name
bc[c4d.DESC_GUIOPEN] = True # expanded by default
bc[c4d.DESC_COLUMNS] = 1 # single column
bc[c4d.DESC_GROUPTYPE] = c4d.DESC_GROUPTYPE_TAB # make it a tab
element = obj.AddUserData(bc)
return element
def add_float_in_tab(obj, tab):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Tab Float"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 10.0
bc[c4d.DESC_PARENTGROUP] = tab
element = obj.AddUserData(bc)
obj[element] = 3.14
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
# Create a tab group
tab_group = add_tab_group(obj, "Settings Tab")
# Add a float field inside the tab
add_float_in_tab(obj, tab_group)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
- Tabs are created by setting
DESC_GROUPTYPE = c4d.DESC_GROUPTYPE_TAB. - They appear as tabbed sections in the Attribute Manager.
- Great for separating categories like “General”, “Advanced”, “Rendering”.
Cinema 4D Python User Data — Descriptor Flags Examples
🔹 Part 3A: Units
Label: userdata_units
Script:
import c4d
def add_angle_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Rotation Angle"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 360.0
bc[c4d.DESC_UNIT] = c4d.DESC_UNIT_DEGREE # display as degrees
element = obj.AddUserData(bc)
obj[element] = 90.0
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_angle_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_UNITlets you specify how values are displayed (degrees, percent, meters, etc.).- This improves clarity for artists by showing the correct measurement context.
🔹 Part 3B: Animatable Fields
Label: userdata_animatable
Script:
import c4d
def add_animatable_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Animatable Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 100.0
bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON # allow keyframing
element = obj.AddUserData(bc)
obj[element] = 50.0
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_animatable_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_ANIMATEcontrols whether a field can be keyframed.- Use
DESC_ANIMATE_ONto allow animation tracks. - Perfect for parameters that should change over time (e.g., strength, offset).
🔹 Part 3C: Hidden / Disabled Fields
Label: userdata_hidden_disabled
Script:
import c4d
def add_hidden_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Hidden Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 10.0
bc[c4d.DESC_HIDE] = True # hide from UI
element = obj.AddUserData(bc)
obj[element] = 5.0
return element
def add_disabled_userdata(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Disabled Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 10.0
bc[c4d.DESC_DISABLE] = True # disable editing
element = obj.AddUserData(bc)
obj[element] = 2.0
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_hidden_userdata(obj)
add_disabled_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_HIDEremoves the field from the Attribute Manager.DESC_DISABLEgreys out the field, making it read‑only.- Useful for conditional setups where some parameters should not be exposed or editable.
Cinema 4D Python User Data — Advanced GUI Structuring Examples
🔹 Part 4A: Multi‑Column Groups
Label: userdata_group_columns
Script:
import c4d
def add_multi_column_group(obj, name):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = name
bc[c4d.DESC_COLUMNS] = 2 # two columns in UI
element = obj.AddUserData(bc)
return element
def add_fields_in_columns(obj, group):
# First field in column 1
bc1 = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc1[c4d.DESC_NAME] = "Column 1 Value"
bc1[c4d.DESC_PARENTGROUP] = group
obj.AddUserData(bc1)
# Second field in column 2
bc2 = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc2[c4d.DESC_NAME] = "Column 2 Value"
bc2[c4d.DESC_PARENTGROUP] = group
obj.AddUserData(bc2)
def main():
obj = doc.GetActiveObject()
if not obj: return
group = add_multi_column_group(obj, "Two Column Group")
add_fields_in_columns(obj, group)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_COLUMNSdefines how many columns appear in the group.- Useful for compact layouts where related fields can sit side‑by‑side.
🔹 Part 4B: Separator Lines
Label: userdata_separator_line
Script:
import c4d
def add_separator_line(obj):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR)
bc[c4d.DESC_NAME] = "Line Separator"
bc[c4d.DESC_SEPARATORLINE] = True # draw horizontal line
obj.AddUserData(bc)
def main():
obj = doc.GetActiveObject()
if not obj: return
add_separator_line(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_SEPARATORLINE = Trueadds a horizontal line in the UI.- Great for visually dividing sections without creating groups.
🔹 Part 4C: Expanded/Collapsed Groups
Label: userdata_group_expanded
Script:
import c4d
def add_collapsible_group(obj, name, expanded=True):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
bc[c4d.DESC_NAME] = name
bc[c4d.DESC_GUIOPEN] = expanded # True = expanded, False = collapsed
element = obj.AddUserData(bc)
return element
def add_field_in_group(obj, group):
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_STRING)
bc[c4d.DESC_NAME] = "Grouped String"
bc[c4d.DESC_PARENTGROUP] = group
element = obj.AddUserData(bc)
obj[element] = "Inside Group"
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
# Collapsible group, expanded by default
group = add_collapsible_group(obj, "Collapsible Group", expanded=True)
add_field_in_group(obj, group)
c4d.EventAdd()
if __name__=='__main__':
main()
Workflow Notes:
DESC_GUIOPENcontrols whether a group is expanded or collapsed initially.- Helps manage UI clutter by hiding advanced options until needed.
🔹 Fixed Animation Track Example
import c4d
def add_animatable_userdata(obj):
# Create a REAL (float) User Data field
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Animated Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 100.0
bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON
element = obj.AddUserData(bc)
obj[element] = 50.0
# Create an animation track for this User Data field
track = c4d.CTrack(obj, element)
curve = track.GetCurve()
# Add two keys and set their values directly
key1 = curve.AddKey(c4d.BaseTime(0.0))
key1["value"] = 0.0
key2 = curve.AddKey(c4d.BaseTime(2.0))
key2["value"] = 100.0
obj.InsertTrackSorted(track)
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_animatable_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
📘 Cinema 4D Python User Data — Training Chunk: Animation Track
🔹 Chunk Metadata
- Label:
userdata_animation_track - Domain: Cinema 4D Python Scripting
- Topic: User Data field with animation track
- Purpose: Demonstrates how to add a float User Data field and automatically create keyframes for it
🔹 Full Script
import c4d
def add_animatable_userdata(obj):
# Create a REAL (float) User Data field
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Animated Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 100.0
bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON
element = obj.AddUserData(bc)
obj[element] = 50.0
# Create an animation track for this User Data field
track = c4d.CTrack(obj, element)
curve = track.GetCurve()
# Add two keys and set their values directly
key1 = curve.AddKey(c4d.BaseTime(0.0))
key1["value"] = 0.0
key2 = curve.AddKey(c4d.BaseTime(2.0))
key2["value"] = 100.0
obj.InsertTrackSorted(track)
return element
def main():
obj = doc.GetActiveObject()
if not obj: return
add_animatable_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
🔹 Workflow Notes
- DESC_ANIMATE: Ensures the field can be keyframed.
- CTrack: Creates a timeline track for the User Data field.
- Curve.AddKey: Adds keyframes at specific times.
- Key values: Set directly via
key["value"]. - Use case: Perfect for custom rigs where User Data drives animation (e.g., slider controlling strength over time).
🔹 Retrieval Anchors
animation trackuserdata animatableCTrack examplekeyframes on user dataCinema 4D Python animation
📘 Cinema 4D Python User Data — Training Chunk: XPresso Driver
🔹 Metadata
- Label:
userdata_xpresso_driver - Domain: Cinema 4D Python Scripting
- Topic: Exposing User Data in XPresso
- Purpose: Demonstrates how to add a User Data field and connect it to an XPresso Object node as an output port
🔹 Full Script
import c4d
def add_driver_userdata(obj):
# Create a REAL (float) User Data field
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
bc[c4d.DESC_NAME] = "Driver Value"
bc[c4d.DESC_MIN] = 0.0
bc[c4d.DESC_MAX] = 10.0
element = obj.AddUserData(bc)
obj[element] = 5.0
return element
def main():
obj = doc.GetActiveObject()
if not obj:
return
element = add_driver_userdata(obj)
# Create an XPresso tag
xp_tag = obj.MakeTag(c4d.Texpresso)
if xp_tag:
node_master = xp_tag.GetNodeMaster()
root = node_master.GetRoot()
# Create an Object node in XPresso
obj_node = node_master.CreateNode(root, c4d.ID_OPERATOR_OBJECT, None, x=100, y=100)
if obj_node:
# Assign the object reference
obj_node[c4d.ID_OPERATOR_OBJECT] = obj
# Add output port for the User Data field
obj_node.AddPort(c4d.GV_PORT_OUTPUT, element)
c4d.EventAdd()
if __name__=='__main__':
main()
🔹 Workflow Notes
- User Data field: Adds a float slider named “Driver Value.”
- XPresso tag: Created with
obj.MakeTag(c4d.Texpresso). - Object node: Inserted into the XPresso graph and linked to the active object.
- Output port: Exposes the User Data field so it can drive other parameters in XPresso.
- Use case: Ideal for procedural rigs where a custom slider controls other scene elements.
🔹 Retrieval Anchors
xpresso driveruserdata xpressoxpresso port exampleCinema 4D Python xpressoconnect userdata to xpresso
📘 Cinema 4D Python User Data — Training Chunk: Presets
🔹 Metadata
- Label:
userdata_presets - Domain: Cinema 4D Python Scripting
- Topic: User Data with Preset values (cycle/enum)
- Purpose: Demonstrates how to add a User Data field with predefined options using
DESC_CYCLE
🔹 Full Script
import c4d
def add_preset_userdata(obj):
# Create a cycle (enum) User Data field
bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG)
bc[c4d.DESC_NAME] = "Preset Choice"
# DESC_CYCLE must be a BaseContainer with string labels
cycle = c4d.BaseContainer()
cycle.SetString(0, "Low")
cycle.SetString(1, "Medium")
cycle.SetString(2, "High")
bc[c4d.DESC_CYCLE] = cycle
bc[c4d.DESC_DEFAULT] = 1 # Default to "Medium"
element = obj.AddUserData(bc)
obj[element] = 1
return element
def main():
obj = doc.GetActiveObject()
if not obj:
return
add_preset_userdata(obj)
c4d.EventAdd()
if __name__=='__main__':
main()
🔹 Workflow Notes
- DTYPE_LONG: Used for cycle/preset fields.
- DESC_CYCLE: Must be a
BaseContainerpopulated with integer keys and string labels. - DESC_DEFAULT: Sets the default option when the field is created.
- Use case: Perfect for rig controls or mode selectors where you want discrete options (e.g., “Low/Medium/High”).
🔹 Retrieval Anchors
userdata presetscycle enum exampleDESC_CYCLE usageCinema 4D Python presetspreset options in user data