Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
Hello, I'm writing a script and I'd like to write/read data from the C4D scene file. Is this possible? If so, can you please show me to the API documentation or an example? I looked in the c4d.storage documentation, but only saw classes for external files.
Thank you!
Hello,
The function SetDocumentData can only be used to update the document settings.
It does accept an Int as a "type" but if you put something else than DOCUMENTSETTINGS_GENERAL DOCUMENTSETTINGS_MODELING DOCUMENTSETTINGS_DOCUMENT DOCUMENTSETTINGS_ANIMATIONSYSTEM the function will simply do nothing. (and the last one is marked as private)
DOCUMENTSETTINGS_GENERAL
DOCUMENTSETTINGS_MODELING
DOCUMENTSETTINGS_DOCUMENT
DOCUMENTSETTINGS_ANIMATIONSYSTEM
BaseContainer is a tree system, you can see sub-BaseContainer just like a child object in the object manager. So you can have (infinite ?) BaseContainer inside BaseContainer.
If you want to store json data, it's up to you. You can store all elements of your json data or simply store a string.
import c4d PLUGIN_ID = 1234567 MyUniqueId = 456789 def main(): #retrieves the document baseContainer docBC = doc.GetDataInstance() #create a sub-BaseContainer subBc = c4d.BaseContainer() subBc[1000] = "hello" subBc[2000] = "world!" # Add the container to the "main" Container docBC.SetContainer(MyUniqueId, subBc) # Updates the document container doc.SetData(docBC) # Print the values stored in our container. for cid, value in doc.GetDataInstance().GetContainer(MyUniqueId): print cid, value # Print Hello World print doc[MyUniqueId][1000], doc[MyUniqueId][2000] if __name__=='__main__': main()
Cheers, Manuel
Hi,
I am not quite sure what you mean by writing to the c4d file. However, c4d.documents contains several functions for reading and writing scene files. You are probably looking for c4d.documents.LoadDocument() and .SaveDocument().
c4d.documents
c4d.documents.LoadDocument()
.SaveDocument()
Cheers zipit
@zipit Hi, thanks for the reply. I mean saving to data to the scene file, not the scene file itself. For example, when I use the NitroPose plugin, it saves my data for the scene in which it's used and loads it when the file it opened.
to (de-)serialize data outside a scene document you can either use c4d's HyperFile (Link) or just use Python's own means to serialize/pickle data - like the modules json or xml. Using HyperFile has the advantage that it can serialize some cinema specific data types (everything you can put into a BaseContainer) out of the box. But it is binary, so it is not human-readable.
HyperFile
json
xml
BaseContainer
It mostly depends on what it is, that you want to serialize.
@zipit said in Writing data to the .c4d file:
Hi, to (de-)serialize data outside a scene document you can either use c4d's HyperFile (Link) or just use Python's own means to serialize/pickle data - like the modules json or xml. Using HyperFile has the advantage that it can serialize some cinema specific data types (everything you can put into a BaseContainer) out of the box. But it is binary, so it is not human-readable. It mostly depends on what it is, that you want to serialize. Cheers zipit
Thank you, @zipit. As I hinted at in the original post, I'd prefer not to save to an external file. When using the plugin I linked to in my last post, I don't have to load any external files. I click on the plugin tag and the poses I stored are available.
I'm trying to find a way to get this functionality. It can just be some simple text data. Any idea how to do this?
I suppose you mean saving data to the BaseContainer of the BaseDocument? You'll need a plugin id for that so it will not collide with the existing IDs, then you can put a sub-BaseContainer with your data into that container. Or you can define your own datatype.
@blastframe said in Writing data to the .c4d file:
@zipit said in Writing data to the .c4d file: Hi, to (de-)serialize data outside a scene document you can either use c4d's HyperFile (Link) or just use Python's own means to serialize/pickle data - like the modules json or xml. Using HyperFile has the advantage that it can serialize some cinema specific data types (everything you can put into a BaseContainer) out of the box. But it is binary, so it is not human-readable. It mostly depends on what it is, that you want to serialize. Cheers zipit Thank you, @zipit. As I hinted at in the original post, I'd prefer not to save to an external file. When using the plugin I linked to in my last post, I don't have to load any external files. I click on the plugin tag and the poses I stored are available. I'm trying to find a way to get this functionality. It can just be some simple text data. Any idea how to do this? Thank you!
I am more and more confused So, what you actually want, is to save additional data within a c4d scene file? You can do that by registering a PluginID and then save your data under that ID in the documents BaseContainer via BaseDocument.SetDocumentData().
BaseDocument.SetDocumentData()
Fantastic, thank you both for your help!
@Cairyn said in Writing data to the .c4d file:
Here's what I've got so far for the sub-BaseContainer. It doesn't seem to be working. Any ideas about what I'm doing incorrectly?
import c4d PLUGIN_ID = 1234567 MyUniqueId = 1000001 def main(): doc = c4d.documents.GetActiveDocument() #get the document's BaseContainer bc = doc.GetDataInstance() #create a sub-BaseContainer subBc = c4d.BaseContainer() subBc[1000] = "hello" subBc[2000] = "world!" #add the sub-BaseContainer to the document Base Container bc.SetContainer(MyUniqueId, subBc) #set the Document Data using the plugin ID doc.SetDocumentData(PLUGIN_ID, bc) c4d.EventAdd() #get the sub containers data print bc[PLUGIN_ID][2000] if __name__=='__main__': main()
your second example is correct. Implementing your own data type that can be serialized with a BaseContainer, is a feature, that is only available in C++. But with stacking BaseContainers you can do alot.
@zipit
Hi! I updated my code example with line-by-line comments.
It doesn't seem to be working as I can't close the file, then read back the SubContainer's data with this:
#get the sub containers data print bc[PLUGIN_ID][2000]
for cid, value in doc.GetDocumentData(MyUniqueId): print cid, value
and
doc.SetDocumentData(MyUniqueId, subBc)
instead of
bc.SetContainer(MyUniqueId, subBc)
i.e.: Your misconception is that you can get/set all document data in one container. You can't (with this method). You only write/read one settings container at a time. In this case the container you did register.
@zipit Thank you so much for your patience. I changed what you said and
doc.GetDocumentData(MyUniqueId)
does print a BaseContainer, but this doesn't print anything:
Updated script:
import c4d PLUGIN_ID = 1234567 MyUniqueId = 1000001 def main(): doc = c4d.documents.GetActiveDocument() #get the document's BaseContainer bc = doc.GetDataInstance() #create a sub-BaseContainer subBc = c4d.BaseContainer() subBc[1000] = "hello" subBc[2000] = "world!" #add the sub-BaseContainer to the document Base Container doc.SetDocumentData(MyUniqueId, subBc) #set the Document Data using the plugin ID doc.SetDocumentData(PLUGIN_ID, bc) c4d.EventAdd() print doc.GetDocumentData(MyUniqueId) # <c4d.BaseContainer object at 0x0000018F956DC1B0> #get the sub container's data for cid, value in doc.GetDocumentData(MyUniqueId): print cid, value if __name__=='__main__': main()
I'm confused in this example how the SubBaseContainer is being added to the BaseContainer. It seems like they're both being added to the Document.
I might be mistaken, but this was how I envisioned the data structure:
Document ⮧ BaseContainer Data (ID = PLUGIN_ID) ⮧ SubBaseContainer Data (ID = MyUniqueId)
as I said, you can't set the whole container. Or in other words you don't need the variable bc in your example. Here is one one example that shows you how to store hierarchical data and then retrieve it. I wrote this on my iPad, so there might be bugs/typos, but the concept should be clear.
bc
# Your settings container bc = c4d.BaseContainer() # One level of your container, we treat this like a list/folder folder = c4d.BaseContainer() # One item in your folder recipe_1 = c4d.BaseContainer() # Another item in your folder recipe_2 = c4d.BaseContainer() bc[ID_TITLE] = 'My Cooking recipes' bc[ID_DATA] = folder folder[1000] = recipe_1 folder[1001] = recipe_2 recipe_1[ID_TITLE] = 'Choclate delight' recipe_1[ID_INSTRUCTIONS] = 'Lorem Ipsum ...' recipe_2[ID_TITLE] = 'Cheesekake' recipe_2[ID_INSTRUCTIONS] = 'Lorem Ipsum ...' doc.SetDocumentData(ID_MY_SECRET_COOKING_RECIPES, bc) def print_container(bc): ''' ''' for cid, value in bc: print cid, value if isinstance(value, c4d.BaseContainer): print_container(value) print_container(doc.GetDocumentData(ID_MY_SECRET_COOKING_RECIPES))
I'm confused in this example how the SubBaseContainer is being added to the BaseContainer. It seems like they're both being added to the Document. I might be mistaken, but this was how I envisioned the data structure: Document ⮧ BaseContainer Data (ID = PLUGIN_ID) ⮧ SubBaseContainer Data (ID = MyUniqueId)
no, it is
Document --- Stuff --- Other Stuff --- Your ID stuff --- Some clown is saving his cooking recipes here
@zipit Thanks for the explanation of the data structure (and the code for clarity).
I tried my best to get it working, but it's still not printing the data. I'm not sure if it was intentional, but I changed the line
print_container(doc.SetDocumentData(ID_MY_SECRET_COOKING_RECIPES))
as it was throwing an error for not passing a BaseContainer. Did you mean?
print_container(doc.GetDocumentData(ID_MY_SECRET_COOKING_RECIPES))
Regardless it's the same issue where it does see the BaseContainer in the print function, but cannot iterate through it.
import c4d ID_MY_SECRET_COOKING_RECIPES = 2999999 ID_TITLE = 1008 ID_DATA = 1009 ID_INSTRUCTIONS = 1010 def print_container(bc): for cid, value in bc: print cid, value if isinstance(value, c4d.BaseContainer): print_container(value) def main(): doc = c4d.documents.GetActiveDocument() # Your settings container bc = c4d.BaseContainer() # One level of your container, we treat this like a list/folder folder = c4d.BaseContainer() # One item in your folder recipe_1 = c4d.BaseContainer() # Another item in your folder recipe_2 = c4d.BaseContainer() bc[ID_TITLE] = 'My Cooking recipes' bc[ID_DATA] = folder folder[1000] = recipe_1 folder[1001] = recipe_2 recipe_1[ID_TITLE] = 'Choclate delight' recipe_1[ID_INSTRUCTIONS] = 'Lorem Ipsum ...' recipe_2[ID_TITLE] = 'Cheesekake' recipe_2[ID_INSTRUCTIONS] = 'Lorem Ipsum ...' doc.SetDocumentData(ID_MY_SECRET_COOKING_RECIPES, bc) print_container(doc.GetDocumentData(ID_MY_SECRET_COOKING_RECIPES)) if __name__=='__main__': main()
Not sure if this matters, but my ultimate goal would be to save JSON data in the BaseContainer.
@m_magalhaes Hi Manuel and thank you for this contribution. This code's data, however, is not persisting when I do the following:
Instead I get this error for doc[MyUniqueId]:
doc[MyUniqueId]
TypeError: 'NoneType' object has no attribute '__getitem__'
Also, is MyUniqueId supposed to be the PLUGIN_ID? Why is that not being used?
Thanks!