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://developers.maxon.net/forum/topic/6819/7588_store-data-with-the-document
and
https://developers.maxon.net/forum/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, bitmapsfolder = 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_ENABLEDclass 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 Truedef 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 preferencesif __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