Fantastic, thank you both for your help!
Solved Writing data to the .c4d file
@Cairyn said in Writing data to the .c4d file:
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.
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()
Hi,
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.
Cheers
zipit
MAXON SDK Specialist
developers.maxon.net
@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]
Hi,
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.
Cheers
zipit
MAXON SDK Specialist
developers.maxon.net
@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:
for cid, value in doc.GetDocumentData(MyUniqueId):
print cid, value
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)
Hi,
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.
# 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))
MAXON SDK Specialist
developers.maxon.net
@blastframe said in Writing data to the .c4d file:
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)
Hi,
no, it is
Document
--- Stuff
--- Other Stuff
--- Your ID stuff
--- Some clown is saving his cooking recipes here
Cheers
zipit
MAXON SDK Specialist
developers.maxon.net
@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()
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)
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
MAXON SDK Specialist
@m_magalhaes Hi Manuel and thank you for this contribution. This code's data, however, is not persisting when I do the following:
- run the script to set the data
- close and reopen the document.
- comment out the lines setting the data (lines 11-19)
- run the script to print the data
Instead I get this error for 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!
@blastframe For me, it works fine this way. Did you really "close and reopen" the document? Not "save", "close", "load"? Because if you just close the document, the data from the container is gone, which leads to your error message.
The Q regarding MyUniqueId vs. PLUGIN_ID is not relevant (probably typed in a hurry), you can use either. But the ID value should come from Maxon in the end, so you avoid collisions with existing IDs.
hi,
you could use the same ID, you just have to be sure that you are the only one to use that ID to store datas.
That's why we use plugin's ID.
Cheers
Manuel.
MAXON SDK Specialist
@m_magalhaes Thanks, Manuel.
How would I delete the data of an individual sub-BaseContainer in the case of your script?
I've tried several methods below but the doc[MyUniqueId][1000]
still shows data in the print
call.
bc = doc.GetDataInstance().GetContainer(MyUniqueId)
print bc.RemoveData(1000)
# True
print doc[MyUniqueId].RemoveData(1000)
# True
doc[MyUniqueId].FlushAll()
print doc[MyUniqueId][1000]
#hello
I was able to delete all of the data this way, but what if I want to keep the data in subBc[2000]
?
docBC.RemoveData(MyUniqueId)
Thank you!
hello,
there's a difference between GetContainer and GetContainerInstance
The first return a copy of the container the second the original link.
So either you retrieve a copy and set back the container to the parent with SetData
and/or SetContainer
or you retrieve the original link and the changes are reflected immediately.
You can also use del
that is pythonic
bc = doc.GetDataInstance().GetContainerInstance(MyUniqueId)
bc.RemoveData(2000)
#del (bc[2000])
Cheers,
Manuel
MAXON SDK Specialist
@m_magalhaes said in Writing data to the .c4d file:
You can also use
del
that is pythonicbc = doc.GetDataInstance().GetContainerInstance(MyUniqueId) bc.RemoveData(2000) #del (bc[2000])
Wonderful, @m_magalhaes , thank you so much!