Best practice getting all objects in a certain Null
-
Dear Developpers,
I have a script importing files, on which I perform serverall cleanup methods.
Now i want to restrict the methods only on the freshly importet data.
Meaning only getting the objects of a certain null object.
How should i tacle this?break the
getnext()
loop if there is nogetup()
(because imports are always on top ? ) probably not save.Is there a more robuts idea to get all objects of a certain Null ?
kind regards.
mogh -
hi,
there is no function to do it, you must retrieve the next object yourself in the hierarchy. Something like this exemple but you would need to test if the current object is not your null object, otherwise you would go outside the hierarchy.
Cheers,
Manuel -
Thanks @m_magalhaes
yes I can get the name (of the first Object) and check that and getup till I reach it, still I am worried that I can not say for certain what "objecst"
c4d.documents.MergeDocument()
gave me ..."certain Null" is not defined other than it is inserted at the top from C4D ... so i guess I have to get the Null by listing all root nulls bevore and after the import.
Sorry I was a bit unclear and its 2 question in one as I am still "brainstoring"
kind regards
mogh -
hi,
In that case, i would open the document in a memory file structure, run my cleaning functions, save it on the memory file structure and merge the saved document.
if you really need to merge first, i would retrieve the first object of the document before merging the documents and check if i hit it.
Cheers,
Manuel -
As I am already running into problems with my current cleanup which proceses the tree more than once -> I think i have to open the import into "memory file structure" but I am not really clear what you mean from a functional standpoint.
There are several interesting builtin function.
c4d.documents.MergeDocument(doc, name, loadflags, thread) # <- used ATM c4d.documents.LoadFile(name) c4d.documents.LoadDocument(name, loadflags, thread=None) c4d.documents.IsolateObjects(doc, t_objects) # whats the
Whats the difference between
MergeDocument
andLoadDocument
withSCENEFILTER_MERGESCENE
flags ?
Why hasMergeDocument
aSCENEFILTER_MERGESCENE
Flag ?are my asumption right:
- create a new DOC e.g. tempdoc
- LoadFile/ LoadDocument / MergeDocument into that - which one ?
- copy objects or merge doc again ?
thanks in advance
mogh -
my WIP prototype, but the generated cubes are not "present" in the merged document yet.
import c4d from c4d import gui #from c4d.documents import GetActiveDocument def create_obj(doc): obj = c4d.BaseObject(c4d.Ocube) obj.SetName("importedcube") doc.InsertObject(obj) c4d.EventAdd() def main(): doc = c4d.documents.GetActiveDocument() print("Active Doc: ", doc.GetDocumentName()) dirpath = r"c:\\" doctemp = None if doctemp==None: doctemp = c4d.documents.BaseDocument() doctemp.SetDocumentName("temp_import.c4d") doctemp.SetDocumentPath(dirpath) c4d.documents.InsertBaseDocument(doctemp) # calls set active allready #c4d.documents.SetActiveDocument(doctemp) create_obj(doctemp) create_obj(doctemp) """ obj = doc.GetActiveObject() if obj is None: print("No active object") c4d.documents.KillDocument(doctemp) return isolateDoc = c4d.documents.IsolateObjects(doctemp, [obj]) """ mfs = c4d.storage.MemoryFileStruct() mfs.SetMemoryWriteMode() c4d.documents.SaveDocument(doctemp, mfs, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT) data = mfs.GetData() mfs.SetMemoryReadMode(data[0], data[1] ) if c4d.documents.MergeDocument(doc, mfs, loadflags=c4d.SCENEFILTER_MERGESCENE, thread=None ): # c4d.SCENEFILTER_MERGESCENE | c4d.SCENEFILTER_NOUNDO, print("merged", mfs) #doctemp.Flush() c4d.documents.KillDocument(doctemp) #c4d.documents.SetActiveDocument(doc) # Execute main() if __name__=='__main__': c4d.CallCommand(13957) # clear concole main()
-
Hello @mogh,
@m_magalhaes is on a brief vacation, I will quickly jump in here.
Whats the difference between MergeDocument and LoadDocument with SCENEFILTER_MERGESCENE flags ? Why has MergeDocument a SCENEFILTER_MERGESCENE Flag?
As stated in the docs,
SCENEFILTER_MERGESCENE
, primarily meant for plugins. Meant are here importer plugins, the FBX importer plugin will for example make some different decisions when the flag signals that the document to be loaded is meant to be merged with an existing scene. The key flags in SCENEFILTER areOBJECTS
andMATERIALS
when one wants to load in only that aspect of a document. You can also pass in additionallyMERGESCENE
, e.g., in Pythonc4d.SCENEFILTER_MERGESCENE | c4d.SCENEFILTER_OBJECTS
, but it does not make a difference in most cases. TheSCENEFILTER
enum is also used in other contexts thanMergeDocument()
.LoadFile/ LoadDocument / MergeDocument into that - which one ?
That does depend on what you want to do. In principle I would favour them in the order you mentioned them. When possible, I would use
LoadFile()
, as this will give you all the file handling that Cinema 4D has, i.e., you can also open a bitmap in this way. It also perseveres the full scene state of a Cinema 4D file. But it will not give directly aBaseDocument
(one can of course grab it manually viac4d.documents.GetFirstDocument()
and then seraching for it) and will cause the file to be shown in Cinema 4D. When one requires aBaseDocument
and does not want the file to be loaded by Cinema 4D, one should useLoadDocument()
. Finally, when one must merge two files or wants to load only parts of a file, for example its materials, one should useMergeDocument()
, but it will not preserve the active camera of the loaded in file, even when one indicates via the flags that everything should be loaded.Cheers,
Ferdinand -
@ferdinand Thanks, you pointed me to my error:
The above code needs these scene flags to work properly ....
as theMergeDocument()
merges only with certain flags ... (c4d.SCENEFILTER_MERGESCENE
will not work this is a gotcha ...)c4d.documents.MergeDocument(doc, mfs, loadflags=c4d.SCENEFILTER_OBJECTS , thread=None ):
So to update my blueprint:
LoadDocument()
to tempdoc (need to test this ...)- Cleanup
- save tempdoc to
MemoryFileStruct()
- merge "open/active" c4d File with
MemoryFileStruct()
(similar to prototype above)
@SDKTeam
I will update and set to solved when I do get a prototype working. please be patient.thanks
mogh -
Hey mogh,
I also had a look at your code, and I cleaned up some things. This works for me as I would expect it to work.
Cheers,
Ferdinandimport c4d def create_obj(doc): """ """ obj = c4d.BaseObject(c4d.Ocube) obj.SetName("importedcube") doc.InsertObject(obj) def main(): """ """ # I removed a lot of clutter here. temp = c4d.documents.BaseDocument() create_obj(temp) mfs = c4d.storage.MemoryFileStruct() mfs.SetMemoryWriteMode() c4d.documents.SaveDocument(temp, mfs, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT) data = mfs.GetData() mfs.SetMemoryReadMode(data[0], data[1]) # In this case you want to load in the objects and materials I assume. flags = c4d.SCENEFILTER_MERGESCENE | c4.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS if c4d.documents.MergeDocument(doc, mfs, loadflags=flags): print() # You were missing an event add. c4d.EventAdd() # Execute main() if __name__ == '__main__': c4d.CallCommand(13957) # clear concole main()
-
How do I catch / prevent a "ReferenceError: the object 'c4d.documents.BaseDocument' is not alive"
when the user has an unsaved / unclicked / unactive doc as master ?my problem ist the
if not c4d.documents.MergeDocument(doc, mfs, loadflags=flags):
it fails while your
if c4d.documents.MergeDocument(doc, mfs, loadflags=flags):
is error free
thanks
-
Hello @mogh,
@mogh said in Best practice getting all objects in a certain Null:
I will update and set to solved when I do get a prototype working. please be patient.
There is no need to feel rushed. We close threads after 14 days of inactivity, but when users say they want to keep them open when we request a closing, we keep them open much longer. It is only that we will ask every 14 days.
my problem ist the
if not c4d.documents.MergeDocument(doc, mfs, loadflags=flags):
it fails while your
if c4d.documents.MergeDocument(doc, mfs, loadflags=flags):
The negation operator is certainly not the culprit, at least I could not imagine how. A Python object that is not alive anymore, means that the Python bindings and the C++ backend have gone out of sync. Or in other words, there is still a reference to an object in Python but the data it is wrapping living in the C++ backend has been deallocated. You can test an object for being still alive with
c4d.C4DAtom.IsAlive()
.However, when I looked at your script, I first ran into similar problems. The culprit must have been the document inside the MFS, because when I tested
doc
it was still alive, as it was still open in the editor (c4d.documents.BaseDocument
is also a node, i.e., derived fromC4DAtom
). But then the problem vanished, and I could not reproduce it anymore. I do not think that your code is or my code was the issue. I would recommend restarting Cinema 4D and see if the problem then does vanish. Otherwise we have to see if either the MFS orMergeDocument
Python wrappers produces dangling pointers in some cases.FYI: I just ran the code below, i.e., with a
not
, and it runs fine for me.Cheers,
Ferdinandimport c4d def create_obj(doc): """ """ obj = c4d.BaseObject(c4d.Ocube) obj.SetName("importedcube") doc.InsertObject(obj) def main(): """ """ # I removed a lot of clutter here. temp = c4d.documents.BaseDocument() create_obj(temp) mfs = c4d.storage.MemoryFileStruct() mfs.SetMemoryWriteMode() c4d.documents.SaveDocument(temp, mfs, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT) data = mfs.GetData() mfs.SetMemoryReadMode(data[0], data[1]) # In this case you want to load in the objects and materials I assume. flags = c4d.SCENEFILTER_MERGESCENE | c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS if not c4d.documents.MergeDocument(doc, mfs, loadflags=flags): print("Blah") # You were missing an event add. c4d.EventAdd() # Execute main() if __name__ == '__main__': c4d.CallCommand(13957) # clear concole main()
-
Found the problem I tried to Kill a document which was not alive, don't know if this is necessary with a merge.
if c4d.C4DAtom.IsAlive(temp): c4d.documents.KillDocument(temp)
And another gotcha
c4d.documents.SetActiveDocument(temp)
seems to be mandatory if you want to useCallCommand()
-> and do not to forget to set thedoc
back to active after your routine.Thank you