Plugins Search Path from Preferences
-
Hello.
I'm trying to access and subsequently modify Plugins Search Path, which is located in the Preferences window.
I have to accomplish this task via a Python script (not from pyp file).I have a problem at the first step when I'm trying to simply access its value and print it.
import c4d pid, att = None, c4d.plugins.GetFirstPlugin() while att: if 'Plugins/Plugins' in str(att): pid = att.GetID() break att = att.GetNext() prefs = c4d.plugins.FindPlugin(pid) plugins = prefs[c4d.PREF_PLUGINS_PATHS] print(prefs[c4d.PREF_PLUGINS_PATHS])
In this example, it prints an empty string even though several paths are already added.
However, if I open the preferences window, it prints the correct values instead of an empty string. Is it possible to bypass opening the prefs window and make the script work correctly?
-
Hello @merkvilson,
thank you for reaching out to us. That is unfortunately not possible because the preference node for the Plugins section in the preferences is designed in a manner which prevents that. It reads the necessary data only when its
GetDDescription
method is being called. I.e., the plugin paths parameter is only populated once you have opened the preferences dialog, causingGetDDescription
of that node being called. I personally think this is bad design, but our developers seem to disagree, so it probably will stay as is.What you can do is read the values from the
plugins.json
file at the root of the preference directory of the running Cinema 4D instance. The JSON file is however serialized with ourDescribeIO
routines, so it will look something like this for a singular path:{ "identification": "plugins", "content": { "referenceDataType": "net.maxon.interface.datadictionary-C", "_impl": { "_mode": 2, "_data": [ { "dataType": "net.maxon.datatype.id", "content": "searchPaths" }, { "dataType": "(net.maxon.interface.url-C,bool)", "isArray": true, "content": [ { "_0": { "referenceIndex": 0, "referenceDataType": "net.maxon.interface.url-C", "_scheme": "file", "_path": "/Path/To/Plugin/Directory/", "_authority": {}, "_data": {} }, "_1": true } ] } ] } } }
When we decide to change something about the object which is being serialized into this file, currently a
DataDictionary
, also this file will change. I know that this is not a very satisfying solution, but unfortunately the best we can currently offer.For your understanding: When
DesribeIO
is serializing key-value pair types, it puts them into a list in the form:key, value, key, value, key, value, ...
. TheDataDictionary
which is serialized above stores its data under the_data
key. So_data
must here be read in the manner that this dictionary has a keysearchPaths
which stores the following value, which is an array ofUrl, bool
tuples, expressing a plugin path and its activation state, which is then decomposed into its parts.Cheers,
Ferdinand -
Thanks @ferdinand
I wrote this little script for those who want to add one plugin path via the C4D script. I will update it later.
It will NOT overwrite existing paths. Restart is required for instant result.import c4d import os import json def add_plugin_path(path, restart = False): if os.path.exists(path): new_path = path.replace("\\", "/") else: print("Path does not exists") return prefs_folder = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_PREFS) prefs_path = os.path.dirname(prefs_folder) plugins_json_path = os.path.join(prefs_path, 'plugins.json') if os.path.exists(plugins_json_path): with open(plugins_json_path,'r',encoding="utf-8-sig") as plugins: plugins_json_raw = plugins.read() plugins_dict = json.loads(plugins_json_raw) content_dict = plugins_dict["content"]["_impl"]["_data"][1]["content"] # Check if the new path is already in Plugins Path for content_path in content_dict: if os.path.normpath(content_path["_0"]["_path"]) == os.path.normpath(new_path): print(f"'{new_path}' is already in Plugins Path.") return else: plugins_dict = { 'identification': 'plugins', 'content': { 'referenceDataType': 'net.maxon.interface.datadictionary-C', '_impl': { '_mode': 2, '_data': [ { 'dataType': 'net.maxon.datatype.id', 'content': 'searchPaths' }, { 'dataType': '(net.maxon.interface.url-C,bool)', 'isArray': True, 'content': []}]}}} # Create new path content new_content = { "_0": { "referenceIndex": len(plugins_dict["content"]["_impl"]["_data"][1]["content"]), "referenceDataType": "net.maxon.interface.url-C", "_scheme": "file", "_path": new_path, "_authority": {}, "_data": {} }, "_1": True } # Append the new path to the list of paths plugins_dict["content"]["_impl"]["_data"][1]["content"].append(new_content) # Convert the dictionary back to a JSON string updated_plugins_dict = json.dumps(plugins_dict, indent=4) # Write the updated dictionary back to a JSON file with open(plugins_json_path, 'w') as plugins_json: plugins_json.write(updated_plugins_dict) if restart: c4d.RestartMe() custom_path = r"/Path/To/Plugin/Directory/" add_plugin_path(path = custom_path, restart = False)