Storing CommandData plug-in string in a document

On 22/05/2017 at 13:17, xxxxxxxx wrote:

Hello,

I am writing a simple CommandData plug-in, and I need to store a string in the C4D document.

The string will be different for each document, so it's not a global preference, but it's also not attached to any specific object/tag within the document. (Basically it is a path.)

I've hunted around and found these two posts:

https://plugincafe.maxon.net/topic/6819/7588_store-data-with-the-document

and

https://plugincafe.maxon.net/topic/6644/7218_how-to-store-data-with-a-plugin-or-a-script

but I just cannot seem to get the code snippets/concepts outlined within them to work.

Basically, within the "Execute" method of the main command plug-in, I just want to be able to read this string, and in the "Execute" method of a different plug-in, to be able to set it.

I'm trying to use the hinted at code in the second post listed above with the "doc" parameter of the Execute methods:

doc[ MY_PLUG_IN_ID ] = bc

but although setting a string value seems to work (if I check it right after it has been set) when I get to the second Execute method, there's nothing there.

Is this indeed the correct way to approach this problem, or is there another? (something along the lines of c4d.plugins.GetWorldPluginData( MY_PLUG_IN_ID ), but on a document scope?

Thanks for any help/hints/tips,

Charlie

On 23/05/2017 at 09:39, xxxxxxxx wrote:

Hi Charlie,

welcome to the Plugin Café forums 🙂

Actually, I think you are on the right track. The approach sounds about right. And using your own BaseContainer stored under your plugin ID is a good idea to store all your custom data.
Probably there's something wrong with how you actually store or read the data. Can you post those parts of your code, so we can have a look?

On 24/05/2017 at 00:12, xxxxxxxx wrote:

Hello Andreas,

The plugin is (I think) simple enough and basic enough that it's possible to just put all the code up:

import c4d, os, sys
from c4d import plugins, utils, documents, gui, bitmaps

folder = os.path.dirname( __file__ )
if folder not in sys.path: sys.path.insert( 0, folder )

import k

class ToggleDoSave( plugins.CommandData ) :
    
    def Execute( self, doc ) :
        
        preferences = GetPreferences()
        preferences.SetBool( k.PREF_DO_C4D_SAVE_COMMAND, not preferences.GetBool( k.PREF_DO_C4D_SAVE_COMMAND ) )
        
        return True
    
    def GetState( self, doc ) :
        
        preferences = GetPreferences()
        
        if preferences.GetBool( k.PREF_DO_C4D_SAVE_COMMAND ) :
            return c4d.CMD_ENABLED | c4d.CMD_VALUE
        else:
            return c4d.CMD_ENABLED

class GetPath( plugins.CommandData ) :
    
    def Execute( self, doc ) :
        
        documentBaseContainer = GetDocumentPluginData( doc )
        
        defaultString = documentBaseContainer.GetString( k.PATH_FOR_FBX )
        
        print "defaultString in GetPath/Execute: ", defaultString
        
        if defaultString is "": defaultString = doc.GetDocumentName()
        
        # construct a slightly more sensible default etc
        
        path = c4d.gui.InputDialog( "Please enter the path for the FBX", defaultString )
        
        # error check user provided path, eventually have a nice "save file" dialog 
        
        documentBaseContainer.SetString( k.PATH_FOR_FBX, path )
        
        return True
    
class AutomaticFBXExport( plugins.CommandData ) :
    
    def Execute( self, doc ) :

documentBaseContainer = GetDocumentPluginData( doc )
        
        path = documentBaseContainer.GetString( k.PATH_FOR_FBX )
    
        print "path in AutomaticFBXExport: ", path
    
        # error check the path, does it still exist etc ...
    
        preferences = GetPreferences()
        if preferences.GetBool( k.PREF_DO_C4D_SAVE_COMMAND ) : c4d.CallCommand( k.COMMAND_SAVE )

c4d.documents.SaveDocument( doc, path, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, k.C4D_OPTION_SAVE_AS_FBX )
        
        return True

def GetDocumentPluginData( doc ) :
    
    bc = doc[ k.MAIN_COMMAND_PLUGIN_ID ]

print "bc in GetDocumentPluginData: (before is None)", bc
    
    if bc is None:
        
        bc = c4d.BaseContainer( k.MAIN_COMMAND_PLUGIN_ID )
        #bc = c4d.BaseContainer()        ??????

doc[ k.MAIN_COMMAND_PLUGIN_ID ] = bc
        
    print "bc in GetDocumentPluginData: ", bc
    
    return bc
            
def GetPreferences() :
    
    preferences = c4d.plugins.GetWorldPluginData( k.MAIN_COMMAND_PLUGIN_ID )
    
    if preferences == None:

preferences = c4d.BaseContainer( k.MAIN_COMMAND_PLUGIN_ID )
        result = c4d.plugins.SetWorldPluginData( k.MAIN_COMMAND_PLUGIN_ID, preferences )

if preferences.GetBool( k.PREF_DO_C4D_SAVE_COMMAND ) == None: preferences.SetBool( k.PREF_DO_C4D_SAVE_COMMAND, False )
    
    return preferences

if __name__ == "__main__":

theBitmap = bitmaps.BaseBitmap()
    
    theDirectoryPath, theFileName = os.path.split( __file__ )

theBitmap.InitWith( os.path.join( theDirectoryPath, "res", "icon.tif" ) )

plugins.RegisterCommandPlugin( id = k.MAIN_COMMAND_PLUGIN_ID, 
                                   str = k.MAIN_COMMAND_NAME, 
                                   info = 0, 
                                   icon = theBitmap, 
                                   help = k.MAIN_COMMAND_HELP, 
                                   dat = AutomaticFBXExport() )
    
    plugins.RegisterCommandPlugin( id = k.TOGGLE_DO_SAVE_COMMAND_PLUGIN_ID, 
                                   str = k.TOGGLE_DO_SAVE_COMMAND_NAME, 
                                   info = 0, 
                                   icon = None, 
                                   help = k.TOGGLE_DO_SAVE_COMMAND_HELP, 
                                   dat = ToggleDoSave() )
    
    plugins.RegisterCommandPlugin( id = k.GET_PATH_PLUGIN_ID, 
                                   str = k.GET_PATH_NAME, 
                                   info = 0, 
                                   icon = None, 
                                   help = k.GET_PATH_HELP, 
                                   dat = GetPath() )

I hope the general flow is clear: check if a base container with main plugin id exists, create it if it does not, then thereafter use it to store the single string. (The export path for the FBX.)

The method GetDocumentPluginData( doc ) checks for the existence of the base container, creates it if it does not exist.

The method Execute( self, doc ) in the (CommandData) plugin class GetPath uses the above method to get the bc, see if a string is already there, use it if it is, request a string from the user, and store their reply.

The method Execute( self, doc ) in the class AutomaticFBXExport uses the same method to get the bc, gets the string, if the string is empty/bad it will return, if good (and after some error checking to be implemented) it will continue.

My assumption is that although they are different Executes, the same "doc" parameter being fed in, and using the same ID, should give an area of common storage?

All a bit crude at the moment, I know, and it's a very basic plugin (although it will be useful for me working with Unity 3D when it's done) but I cannot work out what I am doing wrong. Usually there's enough hints and tips on these forums from all the kind help that's been given to others over the years that I've been able to work things out, but not this time.

Regards, Charlie

On 24/05/2017 at 10:03, xxxxxxxx wrote:

The problem with your code is, that you get a copy of the BaseContainer returned, when reading it from the document via square braces. Also the document stores a copy, when you set the BaseContainer the first time.
Basically you have two options. Either you write the changed BaseContainer back to the document (after changing the string). Or you use GetContainerInstance() to retrieve the BaseContainer instead of square braces.

Then the relevant code roughly looks like so:

import c4d, os, sys
from c4d import plugins, utils, documents, gui, bitmaps
  
MAIN_COMMAND_PLUGIN_ID = 1234567  # NOTE: Get unique IDs from Plugin Cafe
GET_PATH_PLUGIN_ID     = 1234568  # NOTE: Get unique IDs from Plugin Cafe
PATH_FOR_FBX = 1000 # any will do, as it's an ID in your personal container
  
cnt = 0
  
class GetPath( plugins.CommandData ) :
  
    def Execute( self, doc ) :
        global cnt
        documentBaseContainer = GetDocumentPluginData( doc )
        defaultString = documentBaseContainer.GetString( PATH_FOR_FBX )
        print "GetPath before change: ", defaultString
        path = "An arbitrary string " + str(cnt)
        cnt += 1
        #documentBaseContainer.SetString( PATH_FOR_FBX, path )
        documentBaseContainer[PATH_FOR_FBX] = path  # Note: you can also use square braces to set the string
        print "GetPath after change : ", documentBaseContainer[PATH_FOR_FBX]
        return True
  
class AutomaticFBXExport( plugins.CommandData ) :
  
    def Execute( self, doc ) :
        documentBaseContainer = GetDocumentPluginData( doc )
        path = documentBaseContainer.GetString( PATH_FOR_FBX )
        print "AutomaticFBXExport: ", path
        return True
  
def GetDocumentPluginData( doc ) :
    bc = doc.GetDataInstance().GetContainerInstance( MAIN_COMMAND_PLUGIN_ID )
    if bc is None:
        bc = c4d.BaseContainer() # it's up to you to set the ID of the container, C4D doesn't need this
        doc[ MAIN_COMMAND_PLUGIN_ID ] = bc
        bc = doc.GetDataInstance().GetContainerInstance( MAIN_COMMAND_PLUGIN_ID ) # the doc stores a copy, so you need to get the correct instance again
    return bc
  
if __name__ == "__main__":
    plugins.RegisterCommandPlugin( id = MAIN_COMMAND_PLUGIN_ID,
                                   str = "MAIN COMMAND",
                                   info = 0,
                                   icon = None,
                                   help = "",
                                   dat = AutomaticFBXExport() )
  
    plugins.RegisterCommandPlugin( id = GET_PATH_PLUGIN_ID,
                                   str = "GET PATH",
                                   info = 0,
                                   icon = None,
                                   help = "",
                                   dat = GetPath() )

On 29/05/2017 at 04:00, xxxxxxxx wrote:

Hello Andreas, thank you for that explanation, I was able to get my plug-in completed. It's an interesting gotcha to watch out for if you're not expecting it, but makes perfect sense. Thank you again, regards, Charlie