I have a UserData "MyData" of type Button Cycle and i would like to modify its contents, in python node
Solved XPresso: Modify UserData
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 is c4d.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
MAXON SDK Specialist
developers.maxon.net
@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
MAXON SDK Specialist
developers.maxon.net