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



  • 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.



  • 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



  • @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.



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



  • @PluginStudent Sadly, InsertLast also crashes in Python.



  • 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.



  • @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.)



  • 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.