SOLVED Store generator's property

Hi everyone,

I'm completely misunderstand how to store object's related data.
Read few posts, but there are common words only... Not sure there would be Read/Write/Copy overrides...

I'd like to have a selection tag on the Generator object based on UI's bool (CHECKBOX_ID).
If it's already exist - then use existing, otherwise add it automatically.
Link should be updated after save/open

Please point me to a solution, if any.

CHECKBOX_ID = 1001
SOME_ID = 5000
SOME_ID_TAG = 5001

class GeneratorPlugin(c4d.plugins.ObjectData):
  def Init(self, op):
    if op is None: raise RuntimeError("Failed to retrieves op.")
    self.InitAttr(op, bool, CHECKBOX_ID)
    op[CHECKBOX_ID] = True

    bc = op[SOME_ID]
    if bc is None:
      print "Data Not Exist"
      bc = c4d.BaseContainer()
      bc[SOME_ID] = c4d.BaseContainer()
    else:
      print "Data Exist", bc.GetLink(SOME_ID_TAG)
    op[SOME_ID] = bc

  def GetVirtualObjects(self, op, hh):
    if op is None or hh is None: raise RuntimeError("Failed to retrieves op or hh.")

    tmpSel = c4d.BaseSelect()
    # some calculations

    tag = op[SOME_ID].GetLink(SOME_ID_TAG)
    if op[CHECKBOX_ID]:
      if not tag:
        tag = c4d.SelectionTag(c4d.Tpolygonselection)
        op.InsertTag(tag)
        op[SOME_ID].SetLink(SOME_ID_TAG, tag)
      tmpSel.CopyTo(tag.GetBaseSelect())
    elif tag:
      tag.Remove()
      op[SOME_ID].RemoveData(SOME_ID_TAG)

hello,

The BaseContainer of an object is saved automatically with the document. As long as you are storing your data in the object's BaseContainer, you don't have to worry to save them with the document.

Read/Write/Copy need to be override when you are storing data somewhere else. In most of the case, If one of those function is implemented, you have to implement all of them.

GVO (GetVirtualObjects) isn't the right place to change the scene. See information about threading

To know if you are on the main thread or not you can use GeIsMainThread

The right place to do something like this is the Message function.
You can react to the message MSG_DESCRIPTION_POSTSETPARAMETER to apply some modifications when a parameter have been changed.
You can also react to the message MSG_MENUPREPARE that is sent before the object is inserted in the scene.

Be careful that this selection tag shouldn't be moved from the generator (or it will be reset)
To avoid that, you can change its bit using ChangeNBit

Here are an example of what you want to do.

    def CreateSelectionTag(self, node):
        #Creates a selection tag. Remove the tag first if it already exist
        bc = node.GetDataInstance()
        if bc is None:
            return False
        tag = bc.GetLink(ID_TAGLINK)
        if tag:
            tag.Remove()
            bc.RemoveIndex(ID_TAGLINK)

        tag = c4d.SelectionTag(c4d.Tpolygonselection)
        if tag is None:
            return False
        tag.ChangeNBit(c4d.NBIT_NO_DELETE, c4d.NBITCONTROL_SET);
        tag.ChangeNBit(c4d.NBIT_NO_DD, c4d.NBITCONTROL_SET);
        node.InsertTag(tag)
        bc.SetLink(ID_TAGLINK, tag)
        return True
        

    def RemoveSeletionTag(self, node):
        # Remove the tag 
        bc = node.GetDataInstance()
        if bc is None:
            return False
        tag = bc.GetLink(ID_TAGLINK)
        if tag is None:
            return False
        tag.Remove()
        bc.RemoveIndex(ID_TAGLINK)
        return True

    def Init(self, op):
        op[ID_CHECKBOX] = True
        return True
    
    def Message(self, node, type, data):
        # MSG_MENUPREPARE is sent before the object (generator here) is inserted in the scene. This is a nice moment to add any tag to the node
        if type == c4d.MSG_MENUPREPARE:
            print ("Message mainthread ? ", c4d.threading.GeIsMainThread())
            self.CreateSelectionTag(node)
        # MSG_DESCRIPTION_POSTSETPARAMETER is sent if a parameter have changed. Check here if we should add or remove a tag.
        elif type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
            print ("Message mainthread ? ", c4d.threading.GeIsMainThread())
            if data['descid'][0].id == ID_CHECKBOX:
                if node[ID_CHECKBOX]:
                    return self.CreateSelectionTag(node)
                else:
                    return self.RemoveSeletionTag(node)
        return True

    
    
  
    # Override method, should generate and return the object.
    def GetVirtualObjects(self, op, hierarchyhelp):
        print ("GVO mainthread ? ", c4d.threading.GeIsMainThread())
        if op[ID_CHECKBOX]:
            bc = op.GetDataInstance()
            tag = bc.GetLink(ID_TAGLINK)
            if tag:
                print ("the tag is", tag)
            else:
                print ("this should never happen")
        else:
            bc = op.GetDataInstance()
            tag = bc.GetLink(ID_TAGLINK)
            if tag:
                print ("this should never happen")
            else:
                print ("there's no tag and box isn't checked")


Cheers,
Manuel

Oh, such a rocket science!
Many thanks Manuel, it seems it works super fine.

@m_magalhaes said in Store generator's property:

    if op[ID_CHECKBOX]:
        bc = op.GetDataInstance()
        tag = bc.GetLink(ID_TAGLINK)
        if tag:
            print ("the tag is", tag)
        else:
            print ("this should never happen")
    else:
        bc = op.GetDataInstance()
        tag = bc.GetLink(ID_TAGLINK)
        if tag:
            print ("this should never happen")
        else:
            print ("there's no tag and box isn't checked")

hi

can we considered this thread as resolved ?

Cheers,
Manuel