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

    Problem Adding description parameter by clicking button and access this

    Cinema 4D SDK
    2023 windows python r23
    4
    26
    18.1k
    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.
    • ManuelM
      Manuel
      last edited by

      HI,

      You can also create this new objectData in a new file.

      Remember that you cannot alter the document in the GetVirtualObject function. This function is not executed in the main thread. So, another object could already be reading the document.

      As i said in this thread Ferdinand "demonstrates a pattern to safely modify the scene graph from a generator object'.

      Cheers,
      Manuel

      MAXON SDK Specialist

      MAXON Registered Developer

      ThomasBT 1 Reply Last reply Reply Quote 0
      • ThomasBT
        ThomasB @Manuel
        last edited by ThomasB

        @manuel
        Do you mean just the active document or also a virtual document which is not alive?
        I do not exactly know if that is wrong but when the user starts the plugin for the first time, a layer is created and the node is set to this layer, the layer is saved in an hidden description Baselink and a property which was False in the init function turns to True, and also another hidden LONG Parameter turns to 1,
        this if statement happens just one time in the GVO method...

        or can I call functions also in the node.Init() method? because I need to get op and doc
        The problem is I can´t get the active document from my _init_() method.
        when i write

        self.doc = c4d.documents.GetActiveDocument()
        

        he says, document not alive.
        And when I use it from outer scope that also doesn´t work.
        But the plugin works pretty well...in my opinion. Even if you delete the layer, the next time you press the "Refresh" button he adds again this layer and put all related materials back into this layer....

        I would trigger my Refresh button in my node's description but then he complains "just from the main thread".

        1 Reply Last reply Reply Quote 0
        • ManuelM
          Manuel
          last edited by

          hi,
          This is true for every programming language. When you have two processes accessing the same data, be careful of what you are doing. Just imagine you are looking for a book and at the same time someone else is randomly moving all the book at the same time.

          So, accessing your own document, if there is only one process accessing it, it should be ok to do it inside GVO.

          When you object is added in the object manager, or more precisely, when the command is called from the menu, MSG_MENUPREPARE is sent to the object. If you react to this message in the Message function of the plugin, you can create the layer at that moment. The Message function is (almost) never call outside the main thread.
          Your refresh button is working better because you are reacting to the button pushed in the Message function. That is a safe place to update the scene.

          To be sure your function is called from the mainThread, you can use c4d.threading.GeIsMainThread()

          It is hard to predict all scenario, in some, you will have no issue in other your plugin will break fast and crash c4d.

          Cheers,
          Manuel

          MAXON SDK Specialist

          MAXON Registered Developer

          ThomasBT 1 Reply Last reply Reply Quote 0
          • ThomasBT
            ThomasB @Manuel
            last edited by ThomasB

            @manuel
            sorry manuel for another question depending this and probably annoying you.
            so I want to go the save way:
            so in my quicktabradio for the basement type , the user can choose between 3 different types.
            At the moment the base will cloned and inserted in the virtual document under a null....I can change this so that this is not happening in the GVO method.
            But I need to catch the message for the parameter change in the Quicktabradio for the basetype

            I tried:

                    if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
                        # for key in data:
                        #     print("key:", key)
                        #     print("val:", data[key])
            
                        if data['descid'][0] == c4d.PY_BASE_TYPE:
                            print("yes")
            

            nothing happens

            when I catch a button I catch id with:

                    if type == c4d.MSG_DESCRIPTION_COMMAND:
                        if data['id'][0].id == c4d.PY_ADD_LEVEL:
                            print("Something")
            

            this works.
            so I printed out the dict and get the key "descid" but this doesn't work, how can I catch this Quicktabradio change in my description in the message method?

            Best Regards

            ThomasBT 1 Reply Last reply Reply Quote 0
            • ThomasBT
              ThomasB @ThomasB
              last edited by ThomasB

              @Manuel

              Screenshot 2023-02-25 202007.png
              So I do not really know how to catch this Quicktabradio switch correctly. At the moment I have this in my level object Message() method:
              I tried to catch the parameter "PY_LEVEL_TYPE" of the Level-Plugin and I also tried to catch the MainThread. I don´t know if this is correct 😁

                      def Message(self, node, type, data):    
                          parent = node.GetUp()    
                          if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:    
                              if data["descid"][0].id == c4d.PY_LEVEL_TYPE:    
                                  if c4d.threading.GeIsMainThread():                        
                                      if parent != None:    
                                          c4d.CallButton(parent, c4d.PY_REFRESH) 
              
              

              it works but prints out some errors in the console:

              AttributeError:'function' object has no attribute 'im_func'
              

              I just want, when I switch the Quicktabradio, that the citybuilding-plugins "Refresh" Button is called or to send a Message to the CityBuildings plugin and catch this message and then do something.

              Best Regards

              1 Reply Last reply Reply Quote 0
              • ManuelM
                Manuel
                last edited by Manuel

                Hi,

                We like to have different thread for different question even if it is related to the same project. I forked that thread. The question regarding the license will be discussed on this thread

                @thomasb said in Problem Adding description parameter by clicking button and access this:

                it works, I'd just forgotten to return True in my Message method of the Level-Object

                It is sometimes a bit hard to follow people's project specially when we are bending a bit the rules. I am glad it is working.

                Cheers,
                Manuel

                MAXON SDK Specialist

                MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • ThomasBT
                  ThomasB @Manuel
                  last edited by ThomasB

                  @Manuel said in Problem Adding description parameter by clicking button and access this:

                  Hi,
                  ...........

                  The parameters values are stored (most of the time) in the BaseContainer so be careful, you need to "clean" the BaseContainer each time you delete a level, otherwise it will reload the previous values.

                  .............

                  Cheers,
                  Manuel

                  Sorry Manuel ,
                  it took a while to work out a few things, so now I am ready for your answer.
                  So here is an example plugin, I made it to create description elements by clicking a button. So this example is better for the answer you hopefully can give me.

                  Screenshot 2023-05-11 012148.png
                  I keeped track of the ID`s as you recommended in an array.
                  So when the user clicks the button Add Measurement a new ID will be created in the array. And accordingly to that, a Group with this ID will be created in the description and a Baselink, a real and a Delete Button will be added to the group. Just for example.
                  I have also overwritten Read(), Write() and Copy() methods.

                  And when the user clicks "Delete", the ID will be deleted from the array and so the group and it's parameters dissapear.
                  So when the user clicks again "Add Measurement" then it searches the array and the next smallest ID that is not in the array is used again. To save some ID`s ✌ . God saves the ID's
                  But since this ID already was in the description. It is not empty as you can hardly see in the small video example.

                  You told me something about cleaning the BaseContainer?

                  How can I do that?
                  Here is the code, I think the res file is not necessary for the two buttons in the resfile:

                  Thank you in advance

                  import os
                  import sys
                  import c4d
                  from c4d import plugins, bitmaps
                  import os
                  import copy
                  import math
                  
                  PLUGIN_ID = 1234596549
                  
                  class Supi_Object(plugins.ObjectData):
                  
                      def __init__(self):
                          # self.current_id = 10000
                          self.SetOptimizeCache(True)
                          self.id_list = []
                  
                      def Init(self, op):
                  
                          return True
                  
                      def Read(self, node, hf, level):
                          count = hf.ReadInt32()
                          for index in range(count):
                              value = hf.ReadInt32()
                              self.id_list.append(value)
                  
                          return True
                  
                      def Write(self, node, hf,):
                  
                          count = len(self.id_list)
                          hf.WriteInt32(count)
                  
                          for desc_id in self.id_list:
                              hf.WriteInt32(desc_id)
                  
                          return True
                  
                      def CopyTo(self, dest, snode, dnode, flags, trn):
                  
                          dest.id_list = copy.copy(self.id_list)
                  
                          return True
                  
                      def GetVirtualObjects(self, op, hh):
                          # dirty = True if a cache is dirty or if the data (any parameters) of the object changed.
                          # If nothing changed and a cache is present, return the cache
                  
                          # cache deaktiveren
                  
                          return c4d.BaseObject(c4d.Osplinetext)
                  
                      def GetDDescription(self, op, description, flags):
                  
                          if not description.LoadDescription(op.GetType()):
                              return False
                  
                          # Get description single ID
                          singleID = description.GetSingleDescID()
                  
                          # Check if dynamic group needs to be added
                          bc_group = c4d.GetCustomDataTypeDefault(c4d.DTYPE_GROUP)
                          bc_group.SetBool(c4d.DESC_GUIOPEN, True)
                  
                  
                          # Baselink for Measure
                          bc_baselink = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
                  
                          # Declare REAL parameter container
                          bc_real = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
                          bc_real.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_REALSLIDER)
                          bc_real.SetFloat(c4d.DESC_MIN, 0.0)
                          bc_real.SetFloat(c4d.DESC_MAX, 1.0)
                          bc_real.SetFloat(c4d.DESC_MINSLIDER, 0.0)
                          bc_real.SetFloat(c4d.DESC_MAXSLIDER, 1.0)
                          bc_real.SetFloat(c4d.DESC_STEP, 0.01)
                          bc_real.SetInt32(c4d.DESC_UNIT, c4d.DESC_UNIT_FLOAT)
                          bc_real.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON)
                          bc_real.SetBool(c4d.DESC_REMOVEABLE, False)
                  
                          # Declare Delete Button
                          bc_button = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BUTTON)
                          # bc_button[c4d.DESC_NAME] = "Delete"
                  
                          id_counter = 1
                          for desc_id in self.id_list:
                              # if child.GetType() == 200000082:
                              main_group = c4d.DescID(c4d.DescLevel(desc_id, c4d.DTYPE_GROUP, 0))
                  
                              pm_real = c4d.DescID(c4d.DescLevel(desc_id + id_counter, c4d.DTYPE_REAL, op.GetType()))
                              id_counter += 1
                  
                              measure_link = c4d.DescID(c4d.DescLevel(desc_id + id_counter, c4d.DTYPE_BASELISTLINK, op.GetType()))
                              id_counter += 1
                  
                              button_delete = c4d.DescID(c4d.DescLevel(desc_id + id_counter, c4d.DTYPE_BUTTON, op.GetType()))
                              id_counter = 1
                  
                              group_check = singleID is None
                              if not group_check:
                                  group_check = main_group.IsPartOf(singleID)[0]
                  
                              if group_check:
                                  bc_group.SetString(c4d.DESC_NAME, "Messung" + str(desc_id))
                                  if not description.SetParameter(main_group, bc_group,
                                                                  c4d.DescID(c4d.DescLevel((c4d.ID_OBJECTPROPERTIES)))):
                                      return False
                  
                              pm_check = singleID is None
                              if not pm_check:
                                  pm_check = pm_real.IsPartOf(singleID)[0]
                  
                              if pm_check:
                                  name = "Dynamic REAL "
                                  bc_real.SetString(c4d.DESC_NAME, name)
                                  bc_real.SetString(c4d.DESC_SHORT_NAME, name)
                                  if not description.SetParameter(pm_real, bc_real, main_group):
                                      return False
                  
                              pm_check = singleID is None
                              if not pm_check:
                                  pm_check = measure_link.IsPartOf(singleID)[0]
                  
                              if pm_check:
                                  bc_baselink.SetString(c4d.DESC_NAME, "Measure-Object")
                                  if not description.SetParameter(measure_link, bc_baselink, main_group):
                                      return False
                  
                  
                              pm_check = singleID is None
                              if not pm_check:
                                  pm_check = button_delete.IsPartOf(singleID)[0]
                  
                              if pm_check:
                                  bc_button.SetString(c4d.DESC_NAME, "Delete")
                                  bc_button.SetString(c4d.DESC_SHORT_NAME, "Delete")
                                  bc_button.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_BUTTON)
                                  if not description.SetParameter(button_delete, bc_button, main_group):
                                      return False
                  
                          return True, flags | c4d.DESCFLAGS_DESC_LOADED
                  
                      def Message(self, op, typo, data):
                          
                          if typo == c4d.MSG_DESCRIPTION_COMMAND:
                              if data["id"][0].id == c4d.PY_ADD_MEASUREMENT:
                                  start = 10000
                                  while start in self.id_list:
                                      start += 100
                  
                                  # self.id_list.append(self.current_id)
                                  # self.current_id += 100
                                  self.id_list.append(start)
                  
                                  if c4d.threading.GeIsMainThread():
                                      c4d.CallCommand(1011182)
                                      tool = plugins.FindPlugin(op.GetDocument().GetAction(), c4d.PLUGINTYPE_TOOL)
                                  if tool:
                                      objects = op.GetDocument().GetObjects()
                  
                                      c4d.CallButton(tool, c4d.MDATA_MEASURE_NEW)
                                      c4d.CallButton(tool, c4d.MDATA_MEASURE_CREATEOBJECT)
                                      c4d.EventAdd()
                                      for obj in op.GetDocument().GetObjects():
                                          if obj not in objects:
                                              obj.InsertUnder(op)
                                              op.GetDocument().SetActiveObject(op)
                  
                                  op.Message(c4d.MSG_CHANGE)
                  
                              elif data["id"][0].id == c4d.PY_ADD_TOOL:
                  
                                  if c4d.threading.GeIsMainThread():
                                      c4d.CallCommand(1011182)
                  
                              elif data["id"][0].id - 3 in self.id_list:
                                  desc_id = data["id"][0].id - 3
                                  self.id_list.remove(desc_id)
                                  op.Message(c4d.MSG_CHANGE)
                  
                          return True
                     
                  
                  if __name__ == "__main__":
                  
                      path, file = os.path.split(__file__)
                      file = "icon.tif"
                      new_path = os.path.join(path, "res", file)
                      bitmap = bitmaps.BaseBitmap()
                      bitmap.InitWith(new_path)
                      plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="DM-Measure 2023", g=Supi_Object, description="supi_object",
                                                   icon=bitmap,
                                                   info=c4d.OBJECT_GENERATOR)
                  
                  

                  ferdinandF ManuelM 2 Replies Last reply Reply Quote 0
                  • ferdinandF
                    ferdinand @ThomasB
                    last edited by ferdinand

                    Hey @ThomasB,

                    I would not reuse the identifiers of previously removed parameters (when avoidable) as this does violate the Cinema 4D design pattern that parameter identifiers express a purpose. I.e., a parameter Foo with the ID 1000 should not be relabeled as the parameter Bar (with also the ID 1000) . Which is effectively what you are doing here.

                    As you have discovered yourself, you will then run into value history problems. Other things that can cause problems are undo states that are inexplicable for the user and the preset system.

                    C4DAtom parameter identifiers below the value 1,000 are reserved for Cinema 4D. Plugin identifiers start at the value 1,000,000, we should not go above this value because there could be other plugins which write data under such plugin ID into the data container of our node. Static parameter identifiers for a plugin, i.e., what you define in a header file, usually do not exceed the value 20,000. Which means that we have 980,000 identifier slots left. There is no need for any house keeping with our identifiers, even when we reserve them in blocks.

                    A good pattern to setup header files for dynamic descriptions is this:

                    // Header for Oexample object hook description.
                    
                    #ifndef _OEXAMPLE_H__
                    #define _OEXAMPLE_H__
                    
                    enum
                    {
                      // The identifiers for static parameters.
                      ID_GRP_EXAMPLE_MAIN = 1000,
                      ID_VAL_EXAMPLE_MAIN_DIAMETER,
                      // ...
                      ID_GRP_EXAMPLE_OPTIONS = 2000,
                      ID_VAL_EXAMPLE_OPTIONS_BLAH,
                    
                      // This just a normal enum, we can define here whatever we want, including values that are not 
                      // referenced in the res or str files.
                    
                      // These values express the range in which dynamic IDs can be found, i.e., the lowest ID can
                      // be 10,000 and the highest 20,000. Your plugin has still to conform to that, but it is a good
                      // idea to express such information in the header file of the resource.
                      ID_EXAMPLE_DYNAMIC_IDS_START = 10000,
                      ID_EXAMPLE_DYNAMIC_IDS_END = 20000,
                    
                      // The stride with which "logical parameter blocks" are placed. By just reading the header file
                      // we now know that there are 10,000 dynamic IDs going from 10,000 to 20,000, placed with a stride
                      // of 100, resulting in up to 100 dynamic parameter groups. When this is not enough, you can easily
                      // also set #ID_EXAMPLE_DYNAMIC_IDS_END to 200,000 to have 1000 item groups. Or also make your 
                      // stride smaller. A value of 100 is probably a bit wasteful for a use case of only a handful of
                      // parameters.
                      ID_EXAMPLE_DYNAMIC_IDS_STRIDE = 100
                    
                    }
                    
                    #endif // _OEXAMPLE_H__
                    

                    You should also remember to initialize your dynamically added parameters. Which you do not do at the moment in GetDDescription. Doing this will get rid of the problem of 'lingering' values with your current design, but the other problems would remain.

                    Cheers,
                    Ferdinand

                    MAXON SDK Specialist
                    developers.maxon.net

                    ThomasBT 1 Reply Last reply Reply Quote 0
                    • ThomasBT
                      ThomasB @ferdinand
                      last edited by

                      @ferdinand said in Problem Adding description parameter by clicking button and access this:

                      Plugin identifiers start at the value 1,000,000,

                      you probably meant end or?

                      ok this was my first idea, but my ids starting from 10000
                      Manuel just said above that you have to empty the BaseContainer, so thought not a bad idea at all

                      can you still tell me how to empty the container?
                      if not also ok
                      I mean I certainly need that in other situations too.

                      The user deletes the ID, all parameters in the group must be reset.

                      Have a nice day.

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

                        Hi @ThomasB,

                        you probably meant end or?

                        No, I meant start. The upper limit of plugin IDs, i.e., "end", is simply +2147483648, the maximum value a signed 32 bit integer can take. The largest plugin ID assigned at the time of writing is 1061082:

                        b8f79ad5-0ab9-41ee-84c5-ff8cac990f57-image.png

                        Internally, there are exceptions and Cinema 4D ships with some plugins with an ID below 1,000,000 but we can ignore them. The reason why we must respect plugin IDs is that they can also be used as a globally unique address for data containers. I.e., someone could register ID_MY_DATA: int = 1061083 to write his or her data into the data container of a node implemented by you. The plugin ID is then meant to ensure that your plugin does not accidently overwrite that data. From which follows that parameter identifiers should not exceed the value 999,999.

                        can you still tell me how to empty the container?

                        You are not really meant to delete a value of a node, you are only meant to modify the data model, i.e., add, remove, or modify parameters. You can call BaseContainer.RemoveData on the data container of a node to remove an entry. But that is not really deleting the value (history).

                        As stated before, setting a default value once a "new" parameter has been created will fix your problem, even with your current design. No need for deletion. I am not quite sure why Manuel did recommend this pattern, only he can explain that.

                        Cheers,
                        Ferdinand

                        MAXON SDK Specialist
                        developers.maxon.net

                        ThomasBT 2 Replies Last reply Reply Quote 0
                        • ThomasBT
                          ThomasB @ferdinand
                          last edited by ThomasB

                          @ferdinand

                          Ah plugin ID. Sorry, I thought you meant the IDs for the description parameters. Yes, PluginID. Yes, I didn't take that into account when writing the example code

                          Ok, yes, that would be the best solution to add new unused ids for dynamic ids. I will definitely do that.
                          But if the user would press a dynamically created reset button, could I use BaseContainer.FlushAll() to only reset the values in that ID group?
                          Or do I have to use the SetParameter() Method for all parameters.

                          So if the user has made some settings that don't quite fit and he wants to reset all dynamic parameters in the dynamic group... So they have to be set to the default value.

                          Best Regards
                          Thomas

                          1 Reply Last reply Reply Quote 0
                          • ManuelM
                            Manuel @ThomasB
                            last edited by

                            @ThomasB said in Problem Adding description parameter by clicking button and access this:

                            You told me something about cleaning the BaseContainer?
                            2023-05-11 01-11-45.mp4

                            hi, that is exactly why i was talking about cleaning the BaseContainer, or to initialise correctly the values. It is even worse if you mix datatype.

                            I was thinking of the morph tag and the way he does add or remove morph target using the same IDs.

                            And of course, removing data that you are not using anymore is a good idea.

                            Cheers,
                            Manuel

                            MAXON SDK Specialist

                            MAXON Registered Developer

                            ThomasBT 1 Reply Last reply Reply Quote 0
                            • ThomasBT
                              ThomasB @Manuel
                              last edited by

                              @Manuel
                              and how do I initialise the parameter if the user presses for instance reset.
                              With Atom.SetParameter(), right?

                              1 Reply Last reply Reply Quote 0
                              • ManuelM
                                Manuel
                                last edited by

                                You can use SetParameter or change the value directly in the BaseContainer. Using SetParameter is a better option if you want to move the data anywhere else outside the BaseContainer. If you do so, you will have to override the NodeData.SetDParameter function to handle your data properly. (note the difference, there is a D in that function name)
                                Calling SetParameter will work on both cases, storing your data in a BaseContainer or in your own way.
                                It is always a clever idea to initialise your data.

                                I would rather clean the BaseContainer when you press the "delete" button. That will avoid having a BaseContainer getting bigger and bigger, especially if you copy paste your generator from document to document.

                                Cheers,
                                Manuel

                                MAXON SDK Specialist

                                MAXON Registered Developer

                                ThomasBT 2 Replies Last reply Reply Quote 0
                                • ThomasBT
                                  ThomasB @Manuel
                                  last edited by ThomasB

                                  This post is deleted!
                                  1 Reply Last reply Reply Quote 0
                                  • ThomasBT
                                    ThomasB @Manuel
                                    last edited by ThomasB

                                    This post is deleted!
                                    1 Reply Last reply Reply Quote 0
                                    • ThomasBT
                                      ThomasB @ferdinand
                                      last edited by

                                      @ferdinand
                                      I hope it's ok to delete old posts to which I found the answer myself.

                                      So after I have removed now the Data from the Container.
                                      I also keeped track of the already used ID´s in a member variable, to follow your intructions related the Cinema 4D Design pattern.

                                      But is it now also necessary to overwright Read, Write and CopyTo Methods
                                      Or is this negligible, when loading the scene again.

                                      • For Instance 10000 and 10100 will be created,

                                      • then 10200 will be created and deleted.

                                      • An new ID will be created again and gets the ID 10300. (10200 already was used)

                                      • Then this 10300 will be deleted again.

                                      • The document will be saved.

                                      • After loading the document and creating a new ID , next ID will get 10200.

                                      Does this matter. Or is this also important to keep track of the already used ID´s when loading a new doc, or copying the node instance.
                                      Or does it not matter in this case.

                                      Initializing Dynamic parameters:
                                      I still do not now how to Initialize a dynamically created parameter.
                                      I used the DESC_DEFAULT but this seems not to be the right thing.
                                      I look into the plugin example in the sdk for dynamically created parameters.
                                      But this is not really clear for me where he initializes the ID's. He just appends them to a member variable.

                                      I thought this maybe also works with BaseContainter.SetFloat(c4d.DESC_DEFAULT, 50.0) 😁 but yes, puff cake

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

                                        Hello @ThomasB,

                                        first of all, when you store your data in the data container of the node, you do not have to overwrite NodeData.Read, .Write, and .CopyTo as the data container is automatically serialized and deserialized. These methods are only necessary when you want to serialize things which technically cannot, perform badly, or are otherwise undesirable to be expressed in a BaseContainer format.

                                        The automatic serialization of things in the data container of a node applies to all data, i.e., including your dynamic parameters. This is also why I mentioned the rule that your parameter IDs should not exceed 999,999 because other plugins sometimes store data under their plugin ID in your node.

                                        Regarding the ID order. When a new document has been loaded, all the value history data is newly initialized and reusing IDs would therefore not have the same effect anymore. So, things should not be that bad anymore. In the end, there is no absolute right or wrong here, and it depends and what you do. You could also just add a parameter (without an UI, i.e., just an enum value in the header file) to your node which always holds the last used ID slot.

                                        When you play this game long enough, you will of course run at some point out of IDs. But for a stride of 100 and a possible dynamic ID range of [10,000:999,999] you have 9.899 bins, so users would REALLY have to work for reaching that limit.

                                        I personally would for sure build a fail-safe into my plugin that prevents it from going over the ID-limit. I personally would also ensure that parameters that are visually consecutive in the GUI are also consecutive in the data container, i.e., not reuse IDs. If you wanted to, you could also implement a parameter compacting routine which upon a node being allocated, i.e., when there is no value history, compacts parameters from somthing like [10001, 10004, 10006] to [10001, 10002, 10003] so that you can never run out of IDs. I personally would probably not do that given how unlikely such overflow is, I would simply live with the fact that the plugin would stop the user from adding new parameter groups after 10,000 items.

                                        But you can also just reuse IDs, it all depends on how you implement things.

                                        Cheers,
                                        Ferdinand

                                        MAXON SDK Specialist
                                        developers.maxon.net

                                        1 Reply Last reply Reply Quote 0
                                        • M
                                          m_adam
                                          last edited by

                                          Hello @ThomasB ,

                                          without further questions or postings, we will consider this topic as solved by Friday 02/06/2023 and flag it accordingly.

                                          Thank you for your understanding,
                                          Maxime.

                                          MAXON SDK Specialist

                                          Development Blog, MAXON Registered Developer

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