SOLVED Blank appearing dropdown cycle

Hi guys,

I have a problem with filling a dropdown cycle dynamically with options. Right now I’m doing this with userdata for the sake of simplicity. But in the end I want to use it in a Tag Plugin…

So, the setup is as follows. I have a cube (just to have a dummy host object to test the code with) that holds a python tag. The python tag holds the userdata dropdown cycle with the options I want to choose from. Inside the code I swap out the entries of the dropdown cycle based on the condition whether the fillet switch of the cube is active.
So the endgame here is to react to changes of the host object.

While this is all nice and dandy I encounter a problem I can’t seem to resolve. When ever I switch the fillet of the cube the dropdown cycle entries are changed accordingly BUT after the switch the dropdown cycle somehow seems to remember the last selected index and tries to set an entry with this remembered index as the "selected" one. This obviously fails if the entries being switched are of different length. So the entries are there but the dropdown cycle appears so to speak blank.

So question here is, how can I tell the dropdown cycle after the switch to select from the "new" indices. I’m probably missing something simple here.

Cheers,
Sebastian

import c4d
#Welcome to the world of Python

def main():
    
    options1 = [
        "Option 1",
        "Option 2"
    ]
    
    options2 = [
        "Option 1",
        "Option 2",
        "Option 3",
        "Option 4"
    ]
    
    cube = op.GetObject()
    fillet = cube[c4d.PRIM_CUBE_DOFILLET]
    
    bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_LONG)
    bc.SetString(c4d.DESC_NAME, "Options")
    bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_CYCLE)
    
    cycle = c4d.BaseContainer()
    
    if not fillet:
        for i, option in enumerate(options1):
            cycle.SetString(i, option)
    else:
        for i, option in enumerate(options2):
            cycle.SetString(i, option)
    
    bc.SetContainer(c4d.DESC_CYCLE, cycle)
    op.SetUserDataContainer([c4d.ID_USERDATA, 1], bc)
    
    print op[c4d.ID_USERDATA,1]

Hi @HerrMay the issue is that the value defined for your dropdown is over the available option.
When you do a cycle.SetString(i, option) This means the first parameter is equal to 0 but if your value for the user data is 4 and you create a DropDown that has only the value 0 and 1 then nothing will be displayed.

To fix your issue the best way is to set the value of the dropdown (op[c4d.ID_USERDATA,1] = 0 to set the first value).
But you need to do it only when something changed. In your case, you are rebuilding the UI each time the python script is executed, which may be not optimized at all.
To save the state of the fillet we will use a global variable since they are frame-independent.

import c4d

global filletValue
filletValue = None

def main():
    global filletValue
    options1 = [
        "Option 1",
        "Option 2"
    ]

    options2 = [
        "Option 1",
        "Option 2",
        "Option 3",
        "Option 4"
    ]

    cube = op.GetObject()
    newFillet = cube[c4d.PRIM_CUBE_DOFILLET]
    if newFillet != filletValue:

        bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_LONG)
        bc.SetString(c4d.DESC_NAME, "Options")
        bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_CYCLE)

        cycle = c4d.BaseContainer()

        if not newFillet:
            for i, option in enumerate(options1):
                cycle.SetString(i, option)
        else:
            for i, option in enumerate(options2):
                cycle.SetString(i, option)

        bc.SetContainer(c4d.DESC_CYCLE, cycle)
        op.SetUserDataContainer([c4d.ID_USERDATA, 1], bc)

        if newFillet:
            op[c4d.ID_USERDATA, 1] = 0
        else:
            op[c4d.ID_USERDATA, 1] = 1

        filletValue = newFillet
        print op[c4d.ID_USERDATA,1]

Cheers,
Maxime.

Hi @m_adam ,

of course, stupid me. 🤦 I knew it was something simple I’m missing here. Seems I didn’t see the elephant in the room. :D

This works like a charm now. Thank you!

Allow one follow up question though. If I now want to translate this code to a TagData Plugin I’m pretty sure I don’t wanna use a global variable, do I? Would it be sufficient to make that variable a member variable inside of def __init__(self) of the Tag Plugin class?

Cheers,
Sebastian

Yes it will :)

@m_adam alrighty got it working. 😃
Thanks again. You can close this thread if you like.

Cheers,
Sebastian