UNSOLVED Complete Replica of a Native Objects UI?

Hi,

Normally, you'd have to add specific interger, float, slider UI etc for every parameter. Manually.
Just wondering if there is an an efficient way of doing a complete replica of a native objects UI?

f29957b4-c305-4b9b-902b-183c0b7c5755-image.png

Hey @bentraje,

Thank you for reaching out to us. The answer to this depends on what you mean by complete replica here.

  1. You can simply copy the GUI elements manually in the GUI with User Interface > Copy User Data Interface. I assume you are aware of this, and this is not what you mean. Find an example below.
  2. To dynamically show a node inside another node you need a DescriptionCustomGui. You can use this element in dialogs directly. For descriptions (e.g. user data) you must either use a BaseLink as a standin or implement you own CustomGui which then uses the DescriptionCustomGui. The latter is only possible in C++.

Cheers,
Ferdinand

Copying a set of GUI elements manually:

userdata.gif

Hi @ferdinand

Thanks for the response.
Yep the User Interface > Copy User Data Interface is a handy workflow.
That's what I am looking but in python.

Something like.

user_data_ui = obj_a.copy_user_data_interface()
obj_b.paste_user_data_interface()

Is there such an existing API for this?

===

RE: dynamically show a node
I want it to be dynamic but not always.
I just want to replicate the user data once.

Here's an illustration to the problem

  1. Suppose I have 10 area lights. If I want to change all their intensity, I have to select all of them and change them by hand.
    Not as paintful since I can just assign a selection object.
  2. The limitation of #1 is it assumes all light intesity are the same. How about I change them relatively? Like changing the intensity by 125% than their current value? In this case #1 would fail

This is why I'm trying to create dialog where it replicates the UI of the selected object. Then as I input the value it changes on the relevant objects (i.e. if the selected object is a light it will change the other lights based on a criteria).
Logic wise I can by with that.

It just the replica of UI I'm troubled with. I don't want to hand code the UI for each object type hehe

Hi, @bentraje
For each parameter input box, formula expressions are supported in c4d, x : represents the parameter's current value. If you want to change the intensity by 125% than their current value for each light, you can select all of them and simply input x * 1.25 .
The document for Expanded Formula Entry by Multiple Selections is here:
Snipaste_2022-12-18_04-33-07.png

Hello @bentraje,

Is there such an existing API for this?

user_data_ui = obj_a.copy_user_data_interface()
obj_b.paste_user_data_interface()

It depends on what you would call an API. But there are no high-level functions akin to Copy/SaveGui in your example. But there is of course c4d.C4DAtom.GetDescription and the fact that you can iterate over such description, getting the data container, the "GUI", yielded for a parameter in the node. You could then add each of these description containers as a user data paramater container in your node you want to copy to. Instead of calling c4d.GetCustomDataTypeDefault to get a data type default container and then modifying it, you would use a source node parameter data container yielded by its description to call c4d.BaseList2D.AddUserData.

Adding the tangible parameters itself would be no problem with that. The problem are parameters of type DTYPE_GROUP. They are also yielded with the description, but you would have to remap them, as elements refer to their parent group over DESC_PARENTGROUP. So when you add the parameter Foo which in the source node was in a group with the ID 1000, DTYPE_GROUP, 0 you would have first to add the group to the target node as a user data parameter which will give you a new ID. And then for all parameters which reference that group in DESC_PARENTGROUP, replace the reference with the new ID. I do not have the time to write an example right now, if you need help with this, you would have to wait until the new year.

Alternatively, you could also ignore groups, i.e., ignore all parameters of type DTYPE_GROUP and simply flush all group references. All in all, this can probably get a bit tricky, and you might be better off with the manual approach lined out by @iplai .

Cheers,
Ferdinand

There is a third-party module to serialize and deserialize the user data of any BaseList2D object in python dict format. Here's an example:

c4d.ID_USERDATA: {
    (c4d.ID_USERDATA, 1): {
        c4d.DTYPE_: c4d.DTYPE_REAL,
        c4d.DESC_NAME: 'Data',
        c4d.DESC_SHORT_NAME: 'Data',
        c4d.DESC_MIN: 0,
        c4d.DESC_MAX: 1,
        c4d.DESC_STEP: 0.01,
        c4d.DESC_UNIT: c4d.DESC_UNIT_PERCENT,
        c4d.DESC_CUSTOMGUI: c4d.CUSTOMGUI_REAL,
        c4d.DESC_PARENTGROUP: (700, 5, 0),
    },
    (c4d.ID_USERDATA, 2): {
        c4d.DTYPE_: c4d.DTYPE_GROUP,
        c4d.DESC_NAME: 'Group',
        c4d.DESC_SHORT_NAME: 'Group',
        c4d.DESC_ANIMATE: c4d.DESC_ANIMATE_OFF,
        c4d.DESC_COLUMNS: 1,
        c4d.DESC_TITLEBAR: 1,
        c4d.DESC_DEFAULT: 1,
        c4d.DESC_PARENTGROUP: (),
    },
    (c4d.ID_USERDATA, 3): {
        c4d.DTYPE_: c4d.DTYPE_LONG,
        c4d.DESC_NAME: 'Data',
        c4d.DESC_SHORT_NAME: 'Data',
        c4d.DESC_UNIT: c4d.DESC_UNIT_INT,
        c4d.DESC_CUSTOMGUI: c4d.CUSTOMGUI_LONGSLIDER,
        c4d.DESC_MIN: 0,
        c4d.DESC_MAX: 100,
        c4d.DESC_STEP: 1,
        c4d.DESC_PARENTGROUP: ((700, 5, 0), (2, 1, 0)),
    },
}

Snipaste_2022-12-20_09-45-38.png
The module is not fully tested, but it's enough for me for my daily scripting work. The processing of parameters is implemented in a way described by @ferdinand . Sort of like the .res file used by plugins developing, the json data defines the details of a parameter. If you feel like it, you can implement it yourself by refering to following functions:
DumpParams
DumpParamDetails
DumpUserDataDetails
LoadUserData
...
Considering sometimes there's a huge amount of parameters, I spent a lot of time to judge and handle dirty parameters, namely the parameter has been changed, there is still no perfect way. If you don't care whether the parameter is dirty, problem goes easier. Maybe do some filters by restricting DescLevel.creator to a specific value etc.

@iplai @ferdinand

Thanks for providing details especially the third party API. Seems like a handy utility library 🙂 I'll see what I can do with it.