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


Log in to reply