Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    XPresso: Modify UserData

    Cinema 4D SDK
    3
    8
    1.3k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Passion3DP
      Passion3D
      last edited by Passion3D

      I have a UserData "MyData" of type Button Cycle and i would like to modify its contents, in python node

      1 Reply Last reply Reply Quote 0
      • P
        PluginStudent
        last edited by

        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.

        1 Reply Last reply Reply Quote 0
        • Passion3DP
          Passion3D
          last edited by

          Test.c4d

          1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand
            last edited by ferdinand

            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

            1 Reply Last reply Reply Quote 0
            • Passion3DP
              Passion3D
              last edited by

              @zipit Thanks. I test and keep you informed 😉

              1 Reply Last reply Reply Quote 0
              • Passion3DP
                Passion3D
                last edited by

                @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

                1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand
                  last edited by

                  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

                  1 Reply Last reply Reply Quote 1
                  • Passion3DP
                    Passion3D
                    last edited by

                    @zipit work fine 👍 thanks a lot

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post