Layer Selection Python
-
Hi for everyone! I have an easy code which show the hidden layer but i don't understand how change it for do the same for:
- selected layers
- all layers(not only for children).
Code
import c4d
def main():
doc.StartUndo() root = doc.GetLayerObjectRoot() LayersList = root.GetChildren() for layer in LayersList: doc.AddUndo(c4d.UNDOTYPE_CHANGE,layer) layer [c4d.ID_LAYER_VIEW] = 1 doc.EndUndo() c4d.EventAdd()
if name=='main':
main() -
Check for the active bit by
GetBit(c4d.BIT_ACTIVE)
to test whether a layer is selected.
Layers areGeListNode
s, so you can get from one layer object to the other with the usual functionsGetNext()
,GetDown()
etc. -
Also, you are not looking at "only the children". The object you get from GetLayerObjectRoot() is a GeListHead, not a LayerObject, so you need to go from root either to the first layer in the list by GetDown(), or you get the full layer list on the top level(!) by GetChildren().
Each of these layers in the returned list can have children, though, since layers form a full tree structure. So if you want to visit all layers, you need to recurse into the child lists as well. E.g.:
import c4d def visitLayer(layer): if layer == None: return print (layer.GetName(), "Selected" if layer.GetBit(c4d.BIT_ACTIVE) else "Unselected") visitLayer(layer.GetDown()) visitLayer(layer.GetNext()) def main(): first = doc.GetLayerObjectRoot().GetDown() visitLayer(first) if __name__=='__main__': main()
-
Hello @roman,
thank you for reaching out to us. And thank you @Cairyn for providing an answer. A minor problem with the answer provided by @Cairyn is that it is recursive. We recommend walking node graphs iteratively and not recursively, because of the vastness of node graphs in Cinema 4D and danger of producing stack overflows when walking them recusively. In this case, the case of layers, it is unlikely that one will hit the recursion limit of Python (one thousand recursions by default on most systems¹) or produce a stack overflow in C++ (depends on many things).
Cheers,
Ferdinand¹ The recursion limit of Python is defined and set in
sys
viasys.get/setrecursionlimit
. One can raise the limit of one thousand which is rather conservative, but if it is set to high, it will then fail to prevent what it is intended for: to prevent stack overflows and crashing the Python interpereter. -
This post is deleted! -
@cairyn said in Layer Selection Python:
Also, you are not looking at "only the children". The object you get from GetLayerObjectRoot() is a GeListHead, not a LayerObject, so you need to go from root either to the first layer in the list by GetDown(), or you get the full layer list on the top level(!) by GetChildren().
Each of these layers in the returned list can have children, though, since layers form a full tree structure. So if you want to visit all layers, you need to recurse into the child lists as well. E.g.:
import c4d def visitLayer(layer): if layer == None: return print (layer.GetName(), "Selected" if layer.GetBit(c4d.BIT_ACTIVE) else "Unselected") visitLayer(layer.GetDown()) visitLayer(layer.GetNext()) def main(): first = doc.GetLayerObjectRoot().GetDown() visitLayer(first) if __name__=='__main__': main()
Thank you a lot Cairyn!!!
-
@cairyn, i don't understand just one thing how should i change a code if i want to do it with selected objects not for all.
import c4d
def visitLayer(layer):
if layer == None: return
print (layer.GetName())
doc.AddUndo(c4d.UNDOTYPE_CHANGE, layer)
layer [c4d.ID_LAYER_VIEW] = 1
visitLayer(layer.GetDown())
visitLayer(layer.GetNext())def main():
doc.StartUndo()
first = doc.GetLayerObjectRoot().GetDown()
visitLayer (first)doc.EndUndo() c4d.EventAdd()
if name=='main':
main() -
@roman If you mean handle only the selected layers then you just need to insert a check for the active bit:
print (layer.GetName()) if layer.GetBit(c4d.BIT_ACTIVE) : doc.AddUndo(c4d.UNDOTYPE_CHANGE, layer) layer [c4d.ID_LAYER_VIEW] = 1
If you really mean selected objects, I'm not sure what you mean as your code doesn't refer to any objects?
-
@cairyn
For example, we have 20 layers and i need to change visibility just for 2 layers in this list. That's what i meant when i talked about selected layers.I tried to use same code but it showed to me first layer in list....while i want to work with active layers which i have selected.
And i wanted to ask which command delete layer?
Sorry for stupid questions, but SDK a little bit unobvious.
-
@roman Just insert the code from my last post into the overall script.
Here, I did it for you this time:
import c4d def visitLayer(layer): while layer != None: if layer.GetBit(c4d.BIT_ACTIVE) : doc.AddUndo(c4d.UNDOTYPE_CHANGE, layer) layer [c4d.ID_LAYER_VIEW] = not layer [c4d.ID_LAYER_VIEW] visitLayer(layer.GetDown()) layer = layer.GetNext() def main(): doc.StartUndo() first = doc.GetLayerObjectRoot().GetDown() visitLayer(first) doc.EndUndo() c4d.EventAdd() if __name__=='__main__': main()
This script toggles the editor visibility of the currently selected layers. Execute it twice to get the original visibility state back.
Note:
- Since Ferdinand didn't like the overall recursion, I replaced the next-recursion with a while loop. The depth recursion is still there but if you manage to break the Python stack with your nested layer setup, your amount of layers is sus.
- Layers do not have a method to get a list of selected elements last time I looked, so you need to traverse the whole layer tree and check the
BIT_ACTIVE
for each.
(If you are not happy about the structure of Maxon's API documentation, I do have many beginner-oriented lessons and examples in my course https://www.patreon.com/cairyn -- although Patreon is admittedly not ideal for searching; I really need to make this into a proper book.)
As for deleting a layer, a
LayerObject
is aBaseList2D
which is aGeListNode
, so you can use theRemove
method. Like here:import c4d def visitLayer(layer): while layer != None: nextLayer = layer.GetNext() if layer.GetBit(c4d.BIT_ACTIVE) : doc.AddUndo(c4d.UNDOTYPE_DELETE, layer) layer.Remove() else: visitLayer(layer.GetDown()) layer = nextLayer def main(): doc.StartUndo() first = doc.GetLayerObjectRoot().GetDown() visitLayer(first) doc.EndUndo() c4d.EventAdd() if __name__=='__main__': main()
The infrastructure of C4D will remove the objects from that layer and throw the layer away once there are no more references.
Note: Since the layer tree is a linked structure, you will need to pay extra attention to the tree walking, be it recursion or iteration, when you remove layers out of the structure. That's why the visitLayer function looks a bit different here.
-
@cairyn Thank you again. I will check your patreon