Solved Access Custom User Data By Name (not by ID)


Is there a way to access user data by name and not by ID? Base on this post (May 2018), it is not yet possible. But I'm wondering if there is a recent change that allows so.

I'm trying to write a procedure where
(1) It checks if a user data has been created
(2) If it exist, ignore
(3) If it doesn't exist, create the user data.

That is why I needed the name of user data. Is there a way around this? Or if you have better way of doing this. I am all ears.

Thank you for looking at the problem.

Hi @bentraje this can be done by iterating through the UserData Description Container.
Then you can Check for DESC_NAME like so:

import c4d

def IsUserDataPresentByName(obj, name):
    Check if an user data parameter is already named like that on the passed obj
    :param obj: Any object that can holds User Data
    :param name: the name to looking for
    if op is None:
        raise TypeError("obj is none.")
    if not name or not isinstance(name, str):
        raise TypeError("name is not valid.")
    # Iterate over all users data description
    for userDataId, bc in obj.GetUserDataContainer():
        # Retrieves the current name we iterates
        currentName = bc.GetString(c4d.DESC_NAME)
        # If the name is the same return True
        if currentName == name:
            return True
    # If we ended the loop without returning that means we didn't found our description
    return False
def createIntSliderUserData(obj, value, sliderText=""):
    Create a slider of integer on the given object.
    :param value: int => default value of the slider
    :param sliderText: the name of the slider
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_LONG)
    bc[c4d.DESC_NAME] = sliderText
    bc[c4d.DESC_MIN] = 10
    bc[c4d.DESC_MAX] = 100
    description = obj.AddUserData(bc)
    if description is None:
        raise RuntimeError("Failed to creates a UserData.")
    obj[description] = value
    return description

def main():
    if not IsUserDataPresentByName(op, "Something"):
        createIntSliderUserData(op, 50, "Something")

# Execute main()
if __name__=='__main__':

If you have any question, please let me know.


Thanks for the response. Works as expected. Appreciate the code a lot.

Just a quick question on GetUserDataContainer()
It returns a tuple: First is the DescID and the second Container.

Running your script as default, it returns a description of
((700, 5, 0), (1, 15, 0))

I run a type() command on this and it returns a DescLevel
In the documentation, it gives a parameter description as follows

t_id (int) – Initial value for id.
t_datatype (int) – Initial value for dtype.
t_creator (int) – Initial value for creator.

There are two DescLevels above.
(1) My question for first one (700, 5, 0) is where did it came from? Why 700 and why 5?

(2) For the second one, I get the 1 as the ID. No question here
For the 15, I get this is a data type but the list is erratic. I tried changing data type on creation and it gave me varied result up to 100+. Is there info where it list all data types with the corresponding int value? (Correct me if I'm wrong but the documentation only provides the str value)
For the 0, not sure why it is zero.

P.S. To be honest, I'm not sure if my additional question is going to help me. Just trying to explore :)

Thanks again for looking at my problem.

700 is the ID of the UserData Container (c4d.ID_USERDATA) 5 is the type, as I said it's a container of other objects so 5 is the numerical representation of c4d.DTYPE_SUBCONTAINER.

Value of datatypes are not bound to a given range, see DTYPE. No value is printed in the documentation but you can print the value in the console.

Moreover, in C++ you can also register CustomDataType to retrieves a full list of existing DataType for a c4d instance you can use c4d.plugins.FilterPluginList(c4d.PLUGINTYPE_CUSTOMDATATYPE, True)

I hope it answers your question.

Thanks for the response @m_adam
Have a great day ahead!