Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Issue moving userdata between parents

    Scheduled Pinned Locked Moved PYTHON Development
    2 Posts 0 Posters 296 Views
    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.
    • H Offline
      Helper
      last edited by

      On 02/02/2017 at 14:26, xxxxxxxx wrote:

      I'm trying to move userdata elements between groups, but it fails if the new parent group was created after the child element (the child element ends up on the top level). I'm guessing this is because elements are read in order and so when the specified parent group hasn't been read in it just punts.

      Is there an elegant way to handle this?

      The script below is a simplified example. When run, the GRP group ends up on the top level, but it should be under PARENT\SUB2 in the User Data tab. You can see the correct result if you Manage User Data and then click OK.

        
      import c4d   
      from c4d import gui   
      #Welcome to the world of Python   
        
      def CreateUserDataGroup(obj, name, parentGroup=None, columns=None, shortname=None) :   
          if obj is None: return False   
          if shortname is None: shortname = name   
          bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_GROUP)   
          bc[c4d.DESC_NAME] = name   
          bc[c4d.DESC_SHORT_NAME] = shortname   
          bc[c4d.DESC_TITLEBAR] = 1   
          if parentGroup is not None:   
              bc[c4d.DESC_PARENTGROUP] = parentGroup   
          if columns is not None:   
              bc[c4d.DESC_LAYOUTGROUP] = True   
              bc[c4d.DESC_COLUMNS] = columns   
                
          return obj.AddUserData(bc)       
             
      def CreateUserDataFloat(obj, name, val=0, parentGroup=None, unit=c4d.DESC_UNIT_REAL) :   
          if obj is None: return False   
          bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_REAL)   
          bc[c4d.DESC_NAME] = name   
          bc[c4d.DESC_SHORT_NAME] = name   
          bc[c4d.DESC_DEFAULT] = val   
          bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON   
          bc[c4d.DESC_UNIT] = unit   
          bc[c4d.DESC_CUSTOMGUI] = c4d.CUSTOMGUI_REALSLIDER   
          bc[c4d.DESC_MINSLIDER] = -500   
          bc[c4d.DESC_MAXSLIDER] = 500   
          bc[c4d.DESC_STEP] = 1          
          if parentGroup is not None:   
              bc[c4d.DESC_PARENTGROUP] = parentGroup   
        
          element = obj.AddUserData(bc)   
          obj[element] = val   
          return element   
        
      def main() :   
             
          print "*"*10   
          # Create the initial userdata definition   
          parentgroup = CreateUserDataGroup(op,"PARENT")   
          subgroup = CreateUserDataGroup(op,"SUB",parentgroup)   
          group = CreateUserDataGroup(op,"GRP",subgroup)   
          data = CreateUserDataFloat(op,"FLOAT",0,group)   
             
          # Print the current userdata container   
          ud = op.GetUserDataContainer()   
          for id, bc in ud:   
              print id, bc[c4d.DESC_NAME], bc[c4d.DESC_PARENTGROUP]   
              for i in range(0,id.GetDepth()) :   
                  print "-- %s, %s, %s" % (id[i].id, id[i].dtype, id[i].creator)   
              if id == group:   
                  groupdata = bc   
                     
          c4d.EventAdd()   
             
          print "*"*10   
                     
          # Simulate a later operation, moving subgroup to a new group   
          subgroup2 = CreateUserDataGroup(op,"SUB2",parentgroup)   
          groupdata[c4d.DESC_PARENTGROUP] = subgroup2   
          op.SetUserDataContainer(group,groupdata)   
             
          # Print the current userdata container   
          ud = op.GetUserDataContainer()   
          for id, bc in ud:   
              print id, bc[c4d.DESC_NAME], bc[c4d.DESC_PARENTGROUP]      
              for i in range(0,id.GetDepth()) :   
                  print "-- %s, %s, %s" % (id[i].id, id[i].dtype, id[i].creator)   
                              
          c4d.EventAdd()   
        
      if __name__=='__main__':   
          main()   
      
      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        On 04/02/2017 at 10:05, xxxxxxxx wrote:

        Hi Rick,
        The new tab group you create does not exist yet when you try to set the FLOAT item's container to it. And AFAIK there is no way to make it update and exist without running two separate scripts.
        If you create the new tab group in one script. Then run another script to move the FLOAT item over to it. It should work.

        I've been doing a lot if UD stuff lately too. And one of the things I've learned is that copying containers this way only seems to work well on non child UD items. And that's about it.
        It falls apart when groups are involved.
        So what I am doing now is to create all of my UD items from scratch. Rather than trying to piggyback their container data. It's just not worth the headaches to try and share the container data when groups are involved.

        So for example. If I am copying a specific UD Group from one object to another.
        I don't try to apply their containers to each other. Instead I scrape their container data then use it when creating the UD items from scratch.

        #Copy a specific UD group and it's children to another object  
        #This script copies all of the Group type UserData items to a list array  
        #Then uses that list data to copy only a specific group and it's children to another object  
          
        import c4d  
        def main() :  
            
          source = doc.SearchObject("Cube")  
          if source is None: return  
          target = doc.SearchObject("Sphere")   
          if target is None: return   
            
          doc.AddUndo(c4d.UNDOTYPE_CHANGE, target)  
            
          #The specific UD Group to copy to a different object  
          copiedGrp = "Group1"  #<------ Change this as desired  
            
                
          #The level ID#s for any Groups found in the master UD container  
          groups = []  
              
          s_ud = source.GetUserDataContainer()  
          t_ud = target.GetUserDataContainer()   
            
          deleteFirst = False  
            
          if len(t_ud) == 0:  
              #Create a dummy UD item so the UD container is not empty and lets us add things to it  
              dummybc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_BOOL)  
              target.AddUserData(dummybc)  
                 
          lastItem = len(t_ud)  
          if lastItem == 0:   
              lastItem += 1  
              deleteFirst = True  
            
          for UD_ID, bc in source.GetUserDataContainer() :  
                
              #If we've reached a UD Group  
              #Save it's level to the list array  
              if bc.GetLong(c4d.DESC_CUSTOMGUI) == 0:  
                  groups.append(UD_ID[1].id)  
                    
                              
              level = UD_ID[1].id          
                
              #Get the parent level of each UD item  
              descID = bc.GetData(c4d.DESC_PARENTGROUP)  
              pLevel = descID[-1].id   
                
              for i in xrange(len(groups)) :  
                    
                  #Copy the group's data  
                  if level == groups[i]:  
                     grp = bc.GetClone(c4d.COPYFLAGS_PRIVATE_CONTAINER_COPY_IDENTICAL)   
                     grpName = grp.GetString(c4d.DESC_NAME)  
                     if grpName == copiedGrp:                 
                         target.AddUserData(grp)                                       
                    
                  #If the UD item is the child of the group              
                  if pLevel == groups[i]:                  
          
                      if grpName == copiedGrp:  
                        
                          value = source[c4d.ID_USERDATA, level]  #Copy it's value  
                          name = bc.GetString(c4d.DESC_NAME)  
                          guiType = bc.GetLong(c4d.DESC_CUSTOMGUI)  
                          unit = bc.GetLong(c4d.DESC_UNIT)               
                          dType = bc.GetId()               
                     
                          newID = len(target.GetUserDataContainer()) + 1 #Create a new level#                     
                          newbc = c4d.GetCustomDatatypeDefault(dType)    #Create a new container  
                          newbc[c4d.DESC_NAME] = name                    #Copy the name value           
                          newbc[c4d.DESC_CUSTOMGUI] = guiType            #Copy the type value  
                          newbc[c4d.DESC_UNIT] = unit                    #Copy the unit value                      
                          newbc[c4d.DESC_PARENTGROUP] = target.GetUserDataContainer()[lastItem][0]  
                          entry = target.AddUserData(newbc)              #Add the copied UD item to the target object  
                          #if entry[1].dtype == 19: print "Real slider"                   
                          #if entry[1].dtype == 15: print "Long Slider"     
                          #if entry[1].dtype == 1000481: print "gradient"     
                          #etc...           
                         
                          #Copy the value of the source UD item to the target UD item  
                          #Then update the target's master UD container to reflect the changes  
                          target[entry] = value  
                          target.SetUserDataContainer([c4d.ID_USERDATA, newID], newbc)                  
                            
          #Delete the dummy item if it exists  
          if deleteFirst: target.RemoveUserData(1)  
                        
                    
          #print groups  
            
          c4d.SendCoreMessage(c4d.COREMSG_CINEMA, c4d.BaseContainer(c4d.COREMSG_CINEMA_FORCE_AM_UPDATE))          
          c4d.EventAdd()  
          
        if __name__=='__main__':  
          main()
        

        The support guys might have a better answer for you on Monday.
        But for me personally. Sharing containers the way you're doing it has been nothing but painful for me.

        -ScottA

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