XPresso: Modify UserData
-
I have a UserData "MyData" of type Button Cycle and i would like to modify its contents, in python node
-
Where is that Cycle Button?
Is it on some scene object, on some tag, or even on the Python node itself?
Also, it is typically not the job of the Python node to edit scene elements.
-
-
Hi,
modifying a dynamic description from a (threaded) Python context is IMHO is totally fine. Although admittedly in the context XPresso it will probably yield some weird results, if not implemented very carefully, due to the constant execution of a XPresso graph. To modify the dynamic description of an object (its user data) you will need the methods
BaseList2D.GetUserDataContainer
and.GetUserDataContainer
. The cycle container in a description isc4d.DESC_CYCLE
. Something like this:import c4d def modify_cycle(container): """The specific cycle modification logic. Will add an element in this case. Args: container (c4d.BaseContainer): The c4d.DESC_CYCLE container of an description element. Raises: TypeError: Description Returns: c4d.BaseContainer: The modifed cycle container. """ if not isinstance(container, c4d.BaseContainer): raise TypeError(container) # The next id, could also be done more efficiently with # ``BaseContainer.GetIndexId`` and the length of the container. We # are just adding an ID one after the last ID. new_id = [n for n, _ in container][-1] + 1 container[new_id] = "New Item " + str(new_id) return container def modify_user data(node, identifier=None): """General logic to modify some user data. In this case only modifies cycle elements. Args: node (c4d.BaseList2D): The node with the user data. identifier (int, str or None, optional): Either the name or index of the element to be modified. When None, all cycle elements will be modified. Defaults to None. Raises: TypeError: Description ValueError: Description """ # Sort out some invalid inputs and get the user data container. if not isinstance(node, c4d.BaseList2D): msg = "Expected 'node' to be of type BaseList2D: Received: {type}." raise TypeError(msg.format(type=type(node))) data = node.GetUserDataContainer() if not data: msg = "The user data container of {node} is empty." raise ValueError(msg.format(node=node)) # Go over all user data. index = 1 for descid, item in data: # Step over user data where the cycle field is empty. if item[c4d.DESC_CYCLE] is None: continue # Slightly dicey way to fire our cylce modification function when # an element matches our identifier condition. name = item[c4d.DESC_NAME] if identifier in [name, index, None]: item[c4d.DESC_CYCLE] = modify_cycle(item[c4d.DESC_CYCLE]) # Write the modified data back. One confusing thing about # Set/GetUserDataContainer is that SetUser... sets a single element, # while GetUser... returns the containers for all elements. node.SetUserDataContainer(descid, item) index += 1 def main(): """Entry point. """ modify_user data(op) c4d.EventAdd() if __name__=='__main__': main()
Cheers,
zipit -
@zipit Thanks. I test and keep you informed
-
@zipit
In this test, the modification works well, but the cycle button is not updated.
I added c4d.EventAdd(), but in this case it runs in a loop
Test2.c4d -
Hi,
you never write your data back. Also this is exactly the scenario @PluginStudent and I did warn you about. Your file will continuously execute that Python Xpresso node, which is probably not what you want. You will need some execution condition.
import c4d from c4d import gui def main(): print "Modify ButtonCycle" description = Objet.GetUserDataContainer() for descid, element in description: if descid.GetDepth() < 2: continue if descid[1].id == 2: bc = element[c4d.DESC_CYCLE] if not isinstance(bc, c4d.BaseContainer): return bc.FlushAll() bc[0] = "Modifier" element[c4d.DESC_CYCLE] = bc Objet.SetUserDataContainer(descid, element) # You probably want the element to be selected. Objet[descid] = 0
Cheers,
zipit -
@zipit work fine thanks a lot