SOLVED Working with UserData Python

Cinema_4D_zQTRGGCIVF.png Really weird results.

Okay so I have these functions am using to create this user data's 😒.

def CreateUserDataGroup(obj, name, parentGroup=None, columns=None, shortname=None, defaultopen=None, scale=None ):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_GROUP)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_DEFAULT] = 0
    bc[c4d.DESC_TITLEBAR] = 1
    bc[c4d.DESC_GROUPSCALEV] = 0
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if columns is not None:
        #DESC_COLUMNS VALUE IS WRONG IN 15.057 - SHOULD BE 22
        bc[22] = columns
    if defaultopen != None:
        bc[c4d.DESC_DEFAULT] = 1
    if scale != None:
        bc[c4d.DESC_GROUPSCALEV] = 1

    return obj.AddUserData(bc)

def CreateUserDataLink(obj, name, link, parentGroup=None, shortname=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_BASELISTLINK)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    if link is None:
        pass
    else:
        bc[c4d.DESC_DEFAULT] = link
    bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_OFF
    bc[c4d.DESC_SHADERLINKFLAG] = True
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup

    element = obj.AddUserData(bc)
    obj[element] = link
    return element

def CreateUserDataInteger(obj, name, parentGroup=None, shortname=None, text=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_LONG)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_ANIMATE] = 0
    bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_CYCLEBUTTON
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if text is not None:
        #Create a list of names and put them into a container
        names = c4d.BaseContainer()
        # for x in text.split(","):
        count = 0
        for x in text:
            names.SetString(int(count), str(x))
            count += 1
        bc.SetContainer(c4d.DESC_CYCLE, names)
        bc[c4d.DESC_DEFAULT] = 0

    element = obj.AddUserData(bc)

    return element




def CreateUserDataString(obj, name, parentGroup=None, shortname=None, text=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_STRING)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_ANIMATE] = 0
    bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_STRINGMULTI
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if text is not None:
        bc[c4d.DESC_DEFAULT] = text

    element = obj.AddUserData(bc)
    return element

but when I do Groups work fine so do links But all the others are glitched out.

Until I manually go into the user data and fix make a random edit then the integer worksCinema_4D_gMb1lZskey.gif But as you can see in the gif Multiline string does not get fixed as shown in the image belowCinema_4D_duB7PMhsEH.png

I still plan on adding "Scale V" to the string Userdata so help on that would be helpful

Below is the Simplified version of my code if you just want to copy and paste as see it for yourself

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

def randomColor():
    r = randint(0,255) / 256.0
    g = randint(0,255) / 256.0
    b = randint(0,255) / 256.0
    color = c4d.Vector(r,g,b)
    return color

def CreateUserDataGroup(obj, name, parentGroup=None, columns=None, shortname=None, defaultopen=None, scale=None ):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_GROUP)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_DEFAULT] = 0
    bc[c4d.DESC_TITLEBAR] = 1
    bc[c4d.DESC_GROUPSCALEV] = 0
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if columns is not None:
        #DESC_COLUMNS VALUE IS WRONG IN 15.057 - SHOULD BE 22
        bc[22] = columns
    if defaultopen != None:
        bc[c4d.DESC_DEFAULT] = 1
    if scale != None:
        bc[c4d.DESC_GROUPSCALEV] = 1

    return obj.AddUserData(bc)

def CreateUserDataLink(obj, name, link, parentGroup=None, shortname=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_BASELISTLINK)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    if link is None:
        pass
    else:
        bc[c4d.DESC_DEFAULT] = link
    bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_OFF
    bc[c4d.DESC_SHADERLINKFLAG] = True
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup

    element = obj.AddUserData(bc)
    obj[element] = link
    return element

def CreateUserDataInteger(obj, name, parentGroup=None, shortname=None, text=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_LONG)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_ANIMATE] = 0
    bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_CYCLEBUTTON
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if text is not None:
        #Create a list of names and put them into a container
        names = c4d.BaseContainer()
        # for x in text.split(","):
        count = 0
        for x in text:
            names.SetString(int(count), str(x))
            count += 1
        bc.SetContainer(c4d.DESC_CYCLE, names)
        bc[c4d.DESC_DEFAULT] = 0

    element = obj.AddUserData(bc)

    return element




def CreateUserDataString(obj, name, parentGroup=None, shortname=None, text=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_STRING)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_ANIMATE] = 0
    bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_STRINGMULTI
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if text is not None:
        bc[c4d.DESC_DEFAULT] = text

    element = obj.AddUserData(bc)
    return element


def CreateUserDataPopUP(obj, name, parentGroup=None, shortname=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_POPUP)


def SmartCheckSelectedObjs():
    objs = doc.GetActiveObjects(1)
    if objs is None:
        return None
    elif len(objs) == 2:
        print ("In here 222")
        fixedobjs = []
        FoundAParent = False
        
        count = 0

        for x in objs:
            GetParent = x
            if x.GetUp() != None:
                while GetParent != None:
                    count = count + 1
                    if count == 100:
                        print ("Count break: " + count)

                    print ("Inside whileLOop")
                    GetParent = GetParent.GetUp()
                    print (GetParent)
                    # count = count + 1
                    if GetParent == None:
                        print ("Yesttt ")
                    else:
                        GetParentPrev = GetParent
                    # print ("This should neva happen")
                    FoundAParent =True
                if FoundAParent == True:
                    fixedobjs.append(GetParentPrev)
                else:
                    fixedobjs.append(x)
            else:
                fixedobjs.append(x)


        for elem in fixedobjs:
            if fixedobjs.count(elem) > 1:
                print ("Duplicate found")
                return None

            
        return fixedobjs
    elif len(objs) <= 2 or len(objs) > 15:
        return None
    else:
        print ("Fuck it all broken")


def main():
    CheckRun = doc.SearchObject("EaZyRetarget")

    if CheckRun == None:


        CreateER=c4d.BaseObject(c4d.Onull)
        CreateER[c4d.ID_BASELIST_NAME]="EaZyRetarget"
        CreateER()[c4d.ID_BASELIST_ICON_FILE] = "300000157"
        CreateER[c4d.ID_BASELIST_ICON_COLORIZE_MODE] = 1
        CreateER()[c4d.ID_BASELIST_ICON_COLOR] = randomColor()
        doc.InsertObject(CreateER)

        layerGroup = CreateUserDataGroup(CreateER,"EaZyRetargetMain",c4d.DescID(0), "EaZyRetarget", defaultopen=True, scale=True)
        FsubGroup = CreateUserDataGroup(CreateER,"From",layerGroup,2,defaultopen=True)
        
        ListOfSupportedRigTypes = [
            " EaZyRig_Game\n"
            "1; EaZYRig_Standard\n"
            "2; EaZYRig_Full\n"
            "-1; "
            "3; AccuRig\n"
            "4; Mixamo\n"
            "5; Rococo\n"
            "6; Cascadeur\n"
            "7; UE5\n"
            "8; Custom\n"
        ]


        fromCycleButton = CreateUserDataInteger(CreateER,"From",FsubGroup, None, ListOfSupportedRigTypes)
        
        try:
            print ("IN try")
            checkobjs = SmartCheckSelectedObjs()[0]

            print (checkobjs)
            print ("No try error")
        except:
            print ("Yeppp totally")
            checkobjs = SmartCheckSelectedObjs()

            # checkobjs = None





        
        fromCycleLink = CreateUserDataLink(CreateER,"From", checkobjs,FsubGroup, None)
        
        TsubGroup1 = CreateUserDataGroup(CreateER,"To",layerGroup,2, defaultopen=True)

        ToCyclebutton = CreateUserDataInteger(CreateER,"To",TsubGroup1, None, ListOfSupportedRigTypes)
        
        try:
            checkobjs = SmartCheckSelectedObjs()[1]

            print (checkobjs)
        except:
            checkobjs = SmartCheckSelectedObjs()

            # checkobjs = None





        
        ToCycleLink = CreateUserDataLink(CreateER,"To", checkobjs,TsubGroup1, None)

        QUickGuidesubGroup = CreateUserDataGroup(CreateER,"Quick Guide for Custom",layerGroup,2,defaultopen=False,scale=True)

        msg = "WIP Disclamer:\n This could go Bad really Really Fast\n\n \
            You want to make sure the Same Number of\n \
            lines are avail on both include & exclude.\n\n\
            If there is nothing to exclude just make that line\n\
            a None in the exclude userdata"
        
        QuickGuideVar = CreateUserDataString(CreateER,"Quick Guide for Custom",QUickGuidesubGroup,msg)

        CustomsubGroup1 = CreateUserDataGroup(CreateER,"Custom Rigs",layerGroup,2, defaultopen=False,scale=True)

        # bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP) # Create default container

        # bc[c4d.DESC_NAME] = "EaZyRetargetMain"
        # bc[c4d.DESC_SHORT_NAME] = "EaZyRetarget"
        # bc[c4d.DESC_VERSION] = 3
        # bc[c4d.DESC_ANIMATE] = 0
        # bc[c4d.DESC_COLUMNS] = 1
        # bc[c4d.DESC_TITLEBAR] = True
        # bc[c4d.DESC_DEFAULT] = 1
        # # pass empty DescID
        # bc[c4d.DESC_PARENTGROUP] = c4d.DescID()
        # element = CreateER.AddUserData(bc)     # Add userdata container


        # bc1 = c4d.GetCustomDataTypeDefault(c4d.DTYPE_STRING) # Create default container
        # bc1[c4d.DESC_NAME] = "Name"                        # Rename the entry
        # bc1[c4d.DESC_ANIMATE] = 0
        # bc1[c4d.DESC_PARENTGROUP] = element

        message = "I have created a simple 'EaZyRetarget' object.\n\n\
All you need to do is drag the Parent of your Rig 'DOes not matter if it is a \
Null or your hipBone or the Rootbone' Inside the 'From' and 'To' link userdata\n\n\
and Rerun this Script We will work the work for you "

        gui.MessageDialog(message)

    else:
        print("AAAAA")
        # for descId, bc in CheckRun.GetUserDataContainer():
        #     print ("*"*5,descId,"*"*5)
        #     for key, val in bc:
        #         for txt, i in c4d.__dict__.iteritems():
        #             if key == i and txt[:5]=="DESC_":
        #                 print (txt,"=",val)
        #                 break
        #         else:
        #             print (key,"=",val)

# Execute main()
if __name__=='__main__':
    main()
    c4d.EventAdd()


Hello @eazy5d,

Thank you for your update code. You still did not provide a clear description of what you wanted to happen, at least for me the statement "what I mean Is that I am trying to make this whole multiline string GUI" does not really clarify what was going wrong for you.

I now have debugged your code for you, which is something we usually do not do. Please note that this is an exception. There were three differences in the screenshots provided by you.

  1. The in-code created element has an exceptionally long label, the manually created one a short label.
  2. The in-code created element has the empty string as its value, the manually created one has a longer string as its value.
  3. The in-code created element uses the standard Multi-Line String GUI, the manually created one the Python Multi-Line String GUI.

This was caused by three issues:

  1. You were calling your own function CreateUserDataString incorrectly, passing the (default) value of the element as its label.
  2. You misunderstood the purpose of DESC_DEFAULT; it will not set the value of the element, it will only define what value the element takes when 'Reset to Default' is being invoked on it.
  3. You did not set the DR_MULTILINE_PYTHON flag to turn on the Python GUI.

Find below an annotated and stripped version of your script.

Cheers,
Ferdinand

The result of the script:
5e28356b-d63a-4470-a919-c0291dc0bb96-image.png
The code:

"""Contains your stripped script with annotations.
"""

import c4d

doc: c4d.documents.BaseDocument


def CreateUserDataString(obj: c4d.BaseList2D, name: str, parentGroup: c4d.DescID = None,
                        shortname: str = None, text: str = None, isPython: bool = False) -> c4d.DescID:
   """Creates a multi-line edit user data element on #obj and returns its DescID.
   """
   if obj is None:
       return False

   bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_STRING)
   bc[c4d.DESC_NAME] = name
   # Using Python's short-hand fall-through syntax:
   #   Write #shortname to DESC_SHORT_NAME when shortname != None, else write #name.
   bc[c4d.DESC_SHORT_NAME] = shortname or name
   bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_STRINGMULTI

   bc[c4d.DESC_PARENTGROUP] = parentGroup
   # Setting the default value in the parameter description will not set the value of the
   # parameter.
   bc[c4d.DESC_DEFAULT] = text or ""
   # Set if the MLE should use the Python GUI or not, i.e., a mono space layout with Python
   # syntax highlighting.
   bc[c4d.DR_MULTILINE_PYTHON] = isPython

   # Since #AddUserData cannot fail, it will always return a DescID, we can return the return value
   # of the method directly.
   return obj.AddUserData(bc)


def main():
   """
   """
   # I have cleaned up a bit to make things more readable by removing irrelevant stuff.
   null: c4d.BaseObject = c4d.BaseObject(c4d.Onull)

   # The label and the value for the multi-line edit. I have named the value #text because
   # that is how you named it in your function #CreateUserDataString.
   label: str = "Quick Guide for Custom"
   text: str = "Bob is your uncle!\n" * 5

   # You are here unintentionally violating your own function signature. Your call sets
   # shortname=text and lets the default value kick in for #text. Your function expects five
   # arguments in total, you are only passing four. You are also misunderstanding the purpose
   # of #DESC_DEFAULT. Since you never make use of the returned DescID, the value for this MLE
   # is not being set.
   CreateUserDataString(null, label, None, text)

   # You probably meant to do this call instead, here #text will be set as the default value.
   # I am also passing in the sixth argument I have added, to make the MLE a Python MLE as in
   # your screenshots.
   descId: c4d.DescID = CreateUserDataString(null, label, None, None, text, isPython=True)
   # Write a new value to the parameter.
   null[descId] = text

   doc.InsertObject(null)
   c4d.EventAdd()


if __name__ == '__main__':
   main()

Hello @eazy5d,

Thank you for reaching out to us.

Your Problem

I am not quite sure what you consider going wrong in your example since you clearly state what is not working for you. You are talking about a "multiline string [...] not get[ing] fixed", I assume you are talking here about the indentations in the multi-line string you are using for your "help section". This is caused by how you define the string in Python. Defining a string with the concatenation character "" to split the string definition over multiple lines will include all characters of the next line, e.g., this

msg_a: str = "Bob is your uncle, \
              and Mary is your aunt."
msg_b: str = "Bob is your uncle, \
and Mary is your aunt."

print(f"{msg_a = }")
print(f"{msg_b = }")

will output this:

msg_a = 'Bob is your uncle,               and Mary is your aunt.'
msg_b = 'Bob is your uncle, and Mary is your aunt.'

You are unintentionally including white space characters in this manner in your msg variable definition which then will be reflected in the GUI. To fix this, you must either remove all white space characters as shown for msg_b in my example or use other forms of defining strings.

# Python multi line strings suffer from the same problem but have implicit line breaks. 
msg: str = """Bob is your uncle,
and Mary your aunt."""
print(msg)

# Join a string using the implicit concatenation syntax.
msg: str = ("Bob is your uncle,\n"
              "and Mary your aunt.")
print(msg)

# Join a string using the explicit concatenation syntax.
msg: str = ("Bob is your uncle,\n" +
              "and Mary your aunt.")
print(msg)

These three will all print the same as:

Bob is your uncle,
and Mary your aunt.
Bob is your uncle,
and Mary your aunt.
Bob is your uncle,
and Mary your aunt.
[Finished in 65ms]
Other Problems

You also seem to generate a malformed user data container, because this is how your setup looks for me (notice the misplaced labels).

d042e631-9293-4f5a-ab58-545fae410e1f-image.png

I have not debugged your code for you here, please provide an isolated example when you require further help. We cannot debug your code for you. In case you are interested, I have also shown here how you can build a Python scripting object user data GUI from inside the object. Which will mean that you can just create the object, load the code/preset and are ready to go, opposed to the intermediate step of the script manager script.

Cheers,
Ferdinand

@ferdinand I tried editing my post before, as I was able to get most of it working.

What I consider wrong is not the Indentations in the multi-line string, What I mean Is that I am trying to make this Whole Multiline String GuiCinema_4D_Z7iyD8IxcG.png
Using this code below Which I think is okay

#it's Isolated now
LIne 13-28 and line 51 are all you really need to look at

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

def randomColor():
    r = randint(0,255) / 256.0
    g = randint(0,255) / 256.0
    b = randint(0,255) / 256.0
    color = c4d.Vector(r,g,b)
    return color

def CreateUserDataString(obj, name, parentGroup=None, shortname=None, text=None):
    if obj is None: return False
    if shortname is None: shortname = name
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_STRING)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_SHORT_NAME] = shortname
    bc[c4d.DESC_ANIMATE] = 0
    bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_STRINGMULTI
    
    if parentGroup is not None:
        bc[c4d.DESC_PARENTGROUP] = parentGroup
    if text is not None:
        bc[c4d.DESC_DEFAULT] = text

    element = obj.AddUserData(bc)
    return element


# Main function
def main():
    CheckRun = doc.SearchObject("EaZyRetarget")

    if CheckRun == None:


        CreateER=c4d.BaseObject(c4d.Onull)
        CreateER[c4d.ID_BASELIST_NAME]="EaZyRetarget"
        CreateER()[c4d.ID_BASELIST_ICON_FILE] = "300000157"
        CreateER[c4d.ID_BASELIST_ICON_COLORIZE_MODE] = 1
        CreateER()[c4d.ID_BASELIST_ICON_COLOR] = randomColor()
        doc.InsertObject(CreateER)

        msg = "WIP Disclamer:\n This could go Bad really Really Fast\n\n \
            You want to make sure the Same Number of\n \
            lines are avail on both include & exclude.\n\n\
            If there is nothing to exclude just make that line\n\
            a None in the exclude user data"
        
        QuickGuideVar = CreateUserDataString(CreateER,"Quick Guide for Custom",None,msg)

        message = "I have created a simple 'EaZyRetarget' object.\n\n\
All you need to do is drag the Parent of your Rig 'DOes not matter if it is a \
Null or your hipBone or the Rootbone' Inside the 'From' and 'To' link userdata\n\n\
and Rerun this Script We will work the work for you "

        gui.MessageDialog(message)

    else:
        print ("Don't Know don't care")
        
        
        


if __name__=='__main__':
    main()
    c4d.EventAdd()

Here is what I get outputted the whole thing is not useable

Cinema_4D_Ue6MXtGAZ6.png

An isolated code, Expected Behaviour, the Behaviour am getting.

Hello @eazy5d,

Thank you for your update code. You still did not provide a clear description of what you wanted to happen, at least for me the statement "what I mean Is that I am trying to make this whole multiline string GUI" does not really clarify what was going wrong for you.

I now have debugged your code for you, which is something we usually do not do. Please note that this is an exception. There were three differences in the screenshots provided by you.

  1. The in-code created element has an exceptionally long label, the manually created one a short label.
  2. The in-code created element has the empty string as its value, the manually created one has a longer string as its value.
  3. The in-code created element uses the standard Multi-Line String GUI, the manually created one the Python Multi-Line String GUI.

This was caused by three issues:

  1. You were calling your own function CreateUserDataString incorrectly, passing the (default) value of the element as its label.
  2. You misunderstood the purpose of DESC_DEFAULT; it will not set the value of the element, it will only define what value the element takes when 'Reset to Default' is being invoked on it.
  3. You did not set the DR_MULTILINE_PYTHON flag to turn on the Python GUI.

Find below an annotated and stripped version of your script.

Cheers,
Ferdinand

The result of the script:
5e28356b-d63a-4470-a919-c0291dc0bb96-image.png
The code:

"""Contains your stripped script with annotations.
"""

import c4d

doc: c4d.documents.BaseDocument


def CreateUserDataString(obj: c4d.BaseList2D, name: str, parentGroup: c4d.DescID = None,
                        shortname: str = None, text: str = None, isPython: bool = False) -> c4d.DescID:
   """Creates a multi-line edit user data element on #obj and returns its DescID.
   """
   if obj is None:
       return False

   bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_STRING)
   bc[c4d.DESC_NAME] = name
   # Using Python's short-hand fall-through syntax:
   #   Write #shortname to DESC_SHORT_NAME when shortname != None, else write #name.
   bc[c4d.DESC_SHORT_NAME] = shortname or name
   bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_STRINGMULTI

   bc[c4d.DESC_PARENTGROUP] = parentGroup
   # Setting the default value in the parameter description will not set the value of the
   # parameter.
   bc[c4d.DESC_DEFAULT] = text or ""
   # Set if the MLE should use the Python GUI or not, i.e., a mono space layout with Python
   # syntax highlighting.
   bc[c4d.DR_MULTILINE_PYTHON] = isPython

   # Since #AddUserData cannot fail, it will always return a DescID, we can return the return value
   # of the method directly.
   return obj.AddUserData(bc)


def main():
   """
   """
   # I have cleaned up a bit to make things more readable by removing irrelevant stuff.
   null: c4d.BaseObject = c4d.BaseObject(c4d.Onull)

   # The label and the value for the multi-line edit. I have named the value #text because
   # that is how you named it in your function #CreateUserDataString.
   label: str = "Quick Guide for Custom"
   text: str = "Bob is your uncle!\n" * 5

   # You are here unintentionally violating your own function signature. Your call sets
   # shortname=text and lets the default value kick in for #text. Your function expects five
   # arguments in total, you are only passing four. You are also misunderstanding the purpose
   # of #DESC_DEFAULT. Since you never make use of the returned DescID, the value for this MLE
   # is not being set.
   CreateUserDataString(null, label, None, text)

   # You probably meant to do this call instead, here #text will be set as the default value.
   # I am also passing in the sixth argument I have added, to make the MLE a Python MLE as in
   # your screenshots.
   descId: c4d.DescID = CreateUserDataString(null, label, None, None, text, isPython=True)
   # Write a new value to the parameter.
   null[descId] = text

   doc.InsertObject(null)
   c4d.EventAdd()


if __name__ == '__main__':
   main()

Humm thank you for the exception 🤝 I just noticed I stopped my explanation at "GUI" Which totally was not my intention but also must have been frustrating for you. Again Thank you Really good support

Hey @eazy5d,

no worries. We understand that asking clear technical questions can be a non-trivial task, as oneself is already "in the picture" and it is then easy to overlook essential information. To effectively answer questions, the SDK Group must know this essential information, which is why we are explicitly asking for it in most threads. This might seem a bit robotic or pedantic from time to time, but is done in the best interest of users, as experience shows that guessing that information often leads to miscommunication.

Cheers,
Ferdinand

Thank you once again I marked as solved