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

    doc.GetLayerObjectRoot().InsertFirst(layer) does not work.

    Cinema 4D SDK
    python r21
    4
    9
    982
    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.
    • CairynC
      Cairyn
      last edited by

      I hate going back to the layers topic so soon again, but there seems to be something wrong with doc.GetLayerObjectRoot().InsertFirst(layer). After executing this function, layer is no longer alive; the layer list in the layer manager is destroyed, and C4D will crash if you try to access the layers through Python after that - in any form, even with a simple print.

      Try executing this function:

      def createLayerSelected():
          layer = c4d.documents.LayerObject()
          if not layer.IsAlive():
              print "Layer not created"
              return
          if layer == None: return
          doc.GetLayerObjectRoot().InsertFirst(layer)
          if not layer.IsAlive():
              print "Layer not alive after insertion"
              return
          layer[c4d.ID_BASELIST_NAME] = "New Layer"
          layer[c4d.ID_LAYER_COLOR] = c4d.Vector(
                      random.randint(0,256)/256.0,
                      random.randint(0,256)/256.0,
                      random.randint(0,256)/256.0)
          layer[c4d.ID_LAYER_SOLO] = False
          layer[c4d.ID_LAYER_VIEW] = True
          layer[c4d.ID_LAYER_RENDER] = True
          layer[c4d.ID_LAYER_MANAGER] = True
          layer[c4d.ID_LAYER_ANIMATION] = True
          layer[c4d.ID_LAYER_GENERATORS] = True
          layer[c4d.ID_LAYER_DEFORMERS] = True
          layer[c4d.ID_LAYER_EXPRESSIONS] = True
          layer[c4d.ID_LAYER_LOCKED] = False
          layer[c4d.ID_LAYER_XREF] = True
          if not layer.IsAlive():
              print "Layer not alive after attribute setting"
              return
          doc.AddUndo(c4d.UNDOTYPE_NEW, layer)
          if not layer.IsAlive():
              print "Layer not alive after AddUndo"
              return
          # change the layer for all selected objects
          for currentObject in walkSelectedObjects():
              doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, currentObject)
              currentObject.SetLayerObject(layer)
      

      The attribute setting etc pp actually does not matter, as the attempt to insert the new layer causes the trouble already (while the function returns properly, the damage is already done to the layer list).

      Compare with the following version, which does not attempt to insert into the GeListHead but uses a different function to insert before the first existing layer:

      def createLayerSelected2():
          layer = c4d.documents.LayerObject()
          if not layer.IsAlive():
              print "Layer not created"
              return
          if layer == None: return
          insLayer = doc.GetLayerObjectRoot().GetFirst()
          layer.InsertBefore(insLayer)
          if not layer.IsAlive():
              print "Layer not alive after insertion"
              return
          layer[c4d.ID_BASELIST_NAME] = "New Layer"
          layer[c4d.ID_LAYER_COLOR] = c4d.Vector(
                      random.randint(0,256)/256.0,
                      random.randint(0,256)/256.0,
                      random.randint(0,256)/256.0)
          layer[c4d.ID_LAYER_SOLO] = False
          layer[c4d.ID_LAYER_VIEW] = True
          layer[c4d.ID_LAYER_RENDER] = True
          layer[c4d.ID_LAYER_MANAGER] = True
          layer[c4d.ID_LAYER_ANIMATION] = True
          layer[c4d.ID_LAYER_GENERATORS] = True
          layer[c4d.ID_LAYER_DEFORMERS] = True
          layer[c4d.ID_LAYER_EXPRESSIONS] = True
          layer[c4d.ID_LAYER_LOCKED] = False
          layer[c4d.ID_LAYER_XREF] = True
          if not layer.IsAlive():
              print "Layer not alive after attribute setting"
              return
          doc.AddUndo(c4d.UNDOTYPE_NEW, layer)
          if not layer.IsAlive():
              print "Layer not alive after AddUndo"
              return
          # change the layer for all selected objects
          for currentObject in walkSelectedObjects():
              doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, currentObject)
              currentObject.SetLayerObject(layer)
      

      Naturally, this will only work when there is a layer already in the doc's layer list, which makes it unusable as a workaround. (I didn't bother with a None test here.) But the function works fine, creates the layer, inserts the layer, assigns the layer to the selected objects (sorry, walkSelectedObjects is missing as a function here, replace with GetActiveObjects), sets undos properly, and does not encounter errors.
      (Yes, I know that this functionality is identical with the built-in function "New Layer from Object Selection".)

      My conclusion is that doc.GetLayerObjectRoot().InsertFirst(layer) is doing some ugly stuff. I have not tried to use InsertFirst on any other GeListHeads beyond layers yet, maybe that would yield the same crashes.

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

        Hi,

        as far as I understand it, the whole IsAlive testing is unnecessary here, as nodes only die when the hosting context has been freed. A node dies for example when you attach it to a locally allocated document and then pass the node outside that scope without referencing the document somewhere else, so it doesn't get collected by Python's gc. Since you create the node/layer inside the scope you are testing, this seems unnecessary, have you actually encountered layers that were not alive in your function?

        About the insertion stuff: Why not just use the more frontend GeListNode hierarchy methods (InsertUnder for example)? The crashing doesn't sound good though.

        Cheers,
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        CairynC 1 Reply Last reply Reply Quote 0
        • CairynC
          Cairyn @ferdinand
          last edited by

          @zipit Yes, that was the reason I inserted the IsAlive tests... if you run the first code, you will see that the first IsAlive after the InsertFirst fails. The interesting thing is: After creating the layer object, it indeed is alive.

          So, the call to InsertFirst() does not only fail, it breaks the layer object that gets passed, and ruins the whole layer list. That's a bug I'm not too excited about.

          The alternative code

              root = doc.GetLayerObjectRoot()
              layer.InsertUnder(root)
          

          does indeed work. However, since GetLayerObjectRoot() returns a GeListHead which comes with its own set of insertion functions, I do not know how stable this will be in the long term (see also the previous discussion on GeListHead in some other thread).

          Personally I could live with this workaround, but the code is supposed to serve as example for Python learners, so I would prefer to use the intended functions of the proper class instead of replacements from some parent class.

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

            Looking at the documentation, it looks like InsertLast() is typically used.

            CairynC 1 Reply Last reply Reply Quote 0
            • CairynC
              Cairyn @PluginStudent
              last edited by

              @PluginStudent Sadly, InsertLast also crashes in Python.

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

                Hi thanks a lot, @Cairyn for all theses topics, while I'm still searching for the other issue related to GetBranchInfo, this one was easier.
                The issue is located in our code for all GeListHead insertion method in some condition, the ownership is stollen by the python object, so that means at the end of your python scope the c++ layer object is free, while it is not supposed to be free and that makes Cinema 4D crashing.

                The only workaround I can propose is to either be sure that the GeListNode you are inserting is already in a document or use insertion methods from the GeListNode instead.

                Cheers,
                Maxime.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                CairynC 1 Reply Last reply Reply Quote 0
                • CairynC
                  Cairyn @m_adam
                  last edited by Cairyn

                  @m_adam aww, sorry 😊 I should apply for a job at Maxon, then I could comb through (edit: fleece doesn't seem to be right phrase) the source code myself 😁

                  I take away the points:

                  • Sounds as if this was an unknown error, so I suppose S22 does not have a fix for it.
                  • While I didn't try anything else but layers, I gather that these functions cannot be used for any GeListHead/GeListNode combination, whatever the actual child class. Can't exempt an object from the garbage collector after all...
                  • As the document has inserting functions for most classes I use in this context, skipping the access to the GeListHeads in most cases, the issue is limited to layers AFAICS.
                  • I can use the GeListNode's InsertUnder() function to connect a node into a GeListHead. Meanwhile, I tested this method, and it looks as if InsertUnder actually recognizes the GeListHead as such, and breaks (this time correctly) the Up link of the node. So this is a workable workaround.

                  (Regarding the other issue with GetBranchInfo, I had added some posts to that thread (it's really the Tags structure that is affected), just if you have missed it.)

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

                    Hi,

                    Correct it was an unknown error. And yes it not only affect Layer but any kind of GeListNode inserted if they are not already in a hierarchy.

                    I marked this topic as solved, and will bump the topic once a fix is included in a public release.
                    Keep in mind this is a Python only issue.

                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

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

                      This is fixed in R23.

                      Cheers,
                      Maxime.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

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