Issue moving userdata between parents
-
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()
-
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