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 simpleprint
.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 aNone
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 withGetActiveObjects
), 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 useInsertFirst
on any otherGeListHead
s 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 firstIsAlive
after theInsertFirst
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 aGeListHead
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
GeListHead
s in most cases, the issue is limited to layers AFAICS. - I can use the
GeListNode
'sInsertUnder()
function to connect a node into aGeListHead
. Meanwhile, I tested this method, and it looks as ifInsertUnder
actually recognizes theGeListHead
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. -
This is fixed in R23.
Cheers,
Maxime.