Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Writing data to the .c4d file

    Cinema 4D SDK
    r20 python
    4
    26
    15.8k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ?
      A Former User
      last edited by A Former User

      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!

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        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().

        Cheers
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        ? 1 Reply Last reply Reply Quote 0
        • ?
          A Former User @ferdinand
          last edited by A Former User

          @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

          plugin, it saves my data for the scene in which it's used and loads it when the file it opened.

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @A Former User
            last edited by ferdinand

            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

            MAXON SDK Specialist
            developers.maxon.net

            ? 1 Reply Last reply Reply Quote 0
            • ?
              A Former User @ferdinand
              last edited by A Former User

              @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!

              ferdinandF 1 Reply Last reply Reply Quote 0
              • CairynC
                Cairyn
                last edited by

                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.

                ? 2 Replies Last reply Reply Quote 0
                • ferdinandF
                  ferdinand @A Former User
                  last edited by ferdinand

                  @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!

                  Hi,

                  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().

                  Cheers
                  zipit

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • ?
                    A Former User @Cairyn
                    last edited by

                    Fantastic, thank you both for your help!

                    1 Reply Last reply Reply Quote 0
                    • ?
                      A Former User @Cairyn
                      last edited by A Former User

                      @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()
                      
                      1 Reply Last reply Reply Quote 0
                      • ferdinandF
                        ferdinand
                        last edited by ferdinand

                        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

                        ? 1 Reply Last reply Reply Quote 0
                        • ?
                          A Former User @ferdinand
                          last edited by A Former User

                          @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]
                          
                          1 Reply Last reply Reply Quote 0
                          • ferdinandF
                            ferdinand
                            last edited by ferdinand

                            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

                            ? 1 Reply Last reply Reply Quote 1
                            • ?
                              A Former User @ferdinand
                              last edited by A Former User

                              @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()
                              
                              1 Reply Last reply Reply Quote 0
                              • ?
                                A Former User
                                last edited by A Former User

                                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)
                                
                                ferdinandF 1 Reply Last reply Reply Quote 0
                                • ferdinandF
                                  ferdinand
                                  last edited by ferdinand

                                  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

                                  1 Reply Last reply Reply Quote 1
                                  • ferdinandF
                                    ferdinand @A Former User
                                    last edited by

                                    @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

                                    ? 1 Reply Last reply Reply Quote 1
                                    • ?
                                      A Former User @ferdinand
                                      last edited by A Former User

                                      @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()
                                      
                                      1 Reply Last reply Reply Quote 0
                                      • ?
                                        A Former User
                                        last edited by

                                        Not sure if this matters, but my ultimate goal would be to save JSON data in the BaseContainer.

                                        1 Reply Last reply Reply Quote 0
                                        • ManuelM
                                          Manuel
                                          last edited by Manuel

                                          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

                                          MAXON Registered Developer

                                          ? 1 Reply Last reply Reply Quote 2
                                          • ?
                                            A Former User @Manuel
                                            last edited by A Former User

                                            @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!

                                            CairynC 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post