add/remove/modify FieldList with python?
-
Hello, I am having trouble making changes via python to a FieldList existing on a deformer. Ultimately, I will need to flush and procedurally rebuild a FieldList if possible.
As a first test of the API, I tried c4d.FieldList.Flush() and this does not seem to stick- existing FieldLayers remain. Also, I don't see any options to access specific layer?
I feel like I am missing something obvious- any help is appreciated
-
Hello @jenandesign,
Thank you for reaching out to us. Without tangible code of yours, it will be hard to help you. Could it be that you forgot to write your flushed value back? Regarding
FieldList
traversal, which you seem to hint at, you might want to look at this thread here. In short: Field lists are not the monolithic structures they appear to be.Cheers,
FerdinandBefore:
After:
Code:
"""Nukes the field list parameter at #FIELDS of the selected object. """ import c4d doc: c4d.documents.BaseDocument # The active document op: c4d.BaseObject | None # The active object, None if unselected def main() -> None: """Runs the example. """ # Bail when there is no selected object or it has no #FIELDS parameter of type #FieldList. if not op: return data: c4d.FieldList | any = op.GetParameter(c4d.DescID(c4d.FIELDS), c4d.DESCFLAGS_GET_NONE) if not isinstance(data, c4d.FieldList): return # Flush the data and write it back wrapped in an undo. data.Flush() doc.StartUndo() doc.AddUndo(c4d.UNDO_CHANGE, op) op[c4d.FIELDS] = data doc.EndUndo() c4d.EventAdd() if __name__ == '__main__': main()
-
Thanks so much, this points me in the right direction!
Oof, I wish this was more easily accessible/abstracted like c4d.InExcludeData with insert/delete/get/count. Either way I appreciate the tip!
edit 1: Ok, I see that I can just create a new field list and replace the object's data with it, this works.
import c4d doc: c4d.documents.BaseDocument op: c4d.BaseTag def main() -> None: deformer = op[c4d.ID_USERDATA,1] splineGroup = op[c4d.ID_USERDATA,2] fieldList = c4d.FieldList() deformer[c4d.FIELDS] = fieldList c4d.EventAdd() pass
I have a null object
splineGroup
containing severalc4d.SplineObject
that I would like to add as spline field layers to the FieldList stack
edit 2: Digging through, but I don't see how it is possible to add a Spline Field Layer with python.
edit 3: I figured it out
import c4d doc: c4d.documents.BaseDocument op: c4d.BaseTag def main() -> None: deformer = op[c4d.ID_USERDATA,1] splineGroup = op[c4d.ID_USERDATA,2] splines = splineGroup.GetChildren() fieldList = c4d.FieldList() for spline in splines: fieldLayer = c4d.modules.mograph.FieldLayer(c4d.FLspline) fieldLayer.SetLinkedObject(spline) fieldList.InsertLayer(fieldLayer) deformer[c4d.FIELDS] = fieldList c4d.EventAdd() pass
edit 4: Ok, it appears that the script above does not work for multiple splines/layers
edit 5: I know this is probably a simple solve, but it's eluding me.
If I break out the for loop with unique variable names, I can have multiple FieldLayers populating the FieldList.
However, running in a for loop as shown above, I only get one... The last spline in the for loop which is definitely a clue.
-
Hello @jenandesign,
thank you for your follow-up questions. I must unfortunately point out that we do not condone diary-style topics, where developers document various stages of their problem solving. The forum guidelines state:
The initial question of a topic author must be consolidated into a singular posting. Diary style topics with multiple postings are not allowed. See Structure of a Question for more information on how to make a good initial posting. When you want to add information at a later point, please use the edit function.
For an ongoing topic, i.e., information exchange after the initial question, we usually cut everyone some slack, but there are also limits to that. I have consolidated your postings here for you. Please make sure to avoid posting multiple messages in favor of editing your last message. Irrelevant information should also be removed, e.g., code that is not relevant anymore.
About your Question
Please share details of what you are doing. Because we are very accustomed to our APIs, we can of course often guess things, but that tends to be a lot of extra work for us, as we then for example have to write lengthy paragraphs such as this to contextualize our answers.
- What do you want to do?
- Where are you stuck, what is your question?
- Provide a scene file and/or executable code.
When I read here between the lines, I understand that you want to:
- Add spline field layers for all direct spline children of some root object.
- You attempt to do that from inside a Python Programming tag.
Inside the
main
function of a Python Programming tag, you are bound to the threading restrictions of Cinema 4D. Adding or removing scene elements and invoking events it not allowed among other things. Your code violates both restrictions.There are ways to circumvent that problem, you for example can offload work to
NodeData.Message
which most of the time runs on the main thread (i.e., themessage
function inside a Python Programming tag). But that does not alleviate the fundamental logic problem your code has.TagData.Execute
, themain
function, can be called multiple times per scene execution, and a tag is executed at least once per scene execution. Every time the user does something in a document which changes its "state", the scene will be executed and so your tag. So, even when ignoring all the other problems, your code would constantly overwrite the content of the deformer field list, invalidating all manual changes made by a user.Adding things to a scene most of the time either constitutes a command (
CommandData
or a Script Manager script) or a tool (ToolData
,ToolDescriptionData
). There are more fringe plugin types which can be used as for exampleMessageData
, but they are mostly irrelevant.Anything that itself is a scene element, e.g., an object, tag, material, shader, should not modify the state of the scene it is part of. There are ways to circumvent these problems, but this is a case of "if you have to ask, you should not do it". In your case this probably means that you should rewrite your script to work as command/Script Manager script.
Cheers,
FerdinandResult:
Code:"""Demonstrates how to add spline field layers to a field list. THIS SCRIPT VIOLATES THE THREADING RESTRICTIONS OF CINEMA 4D. IT WILL CAUSE CRASHES AND LOSS OF DATA. """ import c4d doc: c4d.documents.BaseDocument op: c4d.BaseTag def main() -> None: """Called by Cinema 4D to execute the tag. """ # Attempt to access our input data. try: deformer: c4d.BaseList2D = op[c4d.ID_USERDATA, 1] null: c4d.BaseList2D = op[c4d.ID_USERDATA, 2] fields: c4d.FieldList = deformer[c4d.FIELDS] if None in (deformer, null): return except: return # At least throttle the output by not constantly overwriting an already populated list. if fields.GetCount() > 0: return # Add the layers and write the data back. root: c4d.GeListHead = fields.GetLayersRoot() for spline in null.GetChildren(): layer: c4d.modules.mograph.FieldLayer = c4d.modules.mograph.FieldLayer(c4d.FLspline) layer.SetLinkedObject(spline) # This is the illegal operation, we insert an element into the scene graph while being # outside of the main thread. Sooner or later this will lead to crashes. layer.InsertUnderLast(root) deformer[c4d.FIELDS] = fields
-
Hello @jenandesign ,
without further questions or postings, we will consider this topic as solved by Friday, the 11th of august 2023 and flag it accordingly.
Thank you for your understanding,
Maxon SDK Group