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 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



  • @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


Log in to reply