Hi @Dunhou, the issue is only a python one since in python there is no Filename type so there is no way for you to return a default value, or you will have no way to read/write the value and therefore store them and value will be lost after each restart.
Regarding bitmap you need to define a BITMAPBUTTON
in the res then in the Message method of your NodeData you should react to the MSG_DESCRIPTION_GETBITMAP and fed the bitmap icon.
So with that's said here an example adapted from py-preference plugin
The .pyp file:
import c4d
# Unique plugin ID obtained from www.plugincafe.com
PLUGIN_ID = 1039699
# Unique plugin ID for world preference container obtained from www.plugincafe.com
WPREF_PYPREFERENCE = 1039700
# ID for the World Preference Container parameter
WPREF_PYPREFERENCE_STRING = 1000
WPREF_PYPREFERENCE_BUTTON = 1001
class PreferenceHelper(object):
@staticmethod
def GetPreferenceContainer():
"""Helper method to retrieve or create the WPREF_PYPREFERENCE container instance stored in the world container.
Returns:
c4d.BaseContainer: The container instance stored in the world container.
Raises:
RuntimeError: The BaseContainer can't be retrieved.
MemoryError: The BaseContainer can't be created.
"""
# Retrieves the world container instance
world = c4d.GetWorldContainerInstance()
if world is None:
raise RuntimeError("Failed to retrieve the world container instance.")
# Retrieves the container of our plugin, stored in the world container instance
# Parameter values will be stored in this container.
bc = world.GetContainerInstance(WPREF_PYPREFERENCE)
# If there is no container, creates one
if bc is None:
# Defines an empty container
world.SetContainer(WPREF_PYPREFERENCE, c4d.BaseContainer())
# Retrieves this empty container instance
bc = world.GetContainerInstance(WPREF_PYPREFERENCE)
if bc is None:
raise MemoryError("Failed to create a BaseContainer.")
return bc
def InitValues(self, descId, description=None):
"""Helper method to define type and default value of parameter
Args:
descId (c4d.DescID): The parameter ID describing the type and the ID of the parameter you want to initialize.
description (c4d.Description, optional): The description of the PreferenceData. Defaults to None.
Returns:
True if success otherwise False.
"""
# Retrieves the world BaseContainer of this preference, where values have to be defined
bc = self.GetPreferenceContainer()
# Defines default values
paramId = descId[0].id
if paramId == c4d.PYPREFERENCE_STRING:
self.InitPreferenceValue(WPREF_PYPREFERENCE_STRING, "File", description, descId, bc)
return True
class Preference(c4d.plugins.PreferenceData, PreferenceHelper):
def Init(self, node):
"""Called by Cinema 4D on the initialization of the PreferenceData, the place to define the type of object.
Args:
node (c4d.GeListNode): The instance of the PreferenceData.
Returns:
True if the initialization success, otherwise False will not create the object.
"""
# Init default values
bc = self.GetPreferenceContainer()
self.InitValues(c4d.DescID(c4d.DescLevel(c4d.PYPREFERENCE_STRING, c4d.DTYPE_STRING, 0)))
return True
def SetDParameter(self, node, id, data, flags):
"""Called by Cinema 4D, when SetParameter is call from the node.
The main purpose is to store the data in the world container.
Args:
node (c4d.GeListNode): The instance of the PreferenceData.
id (c4d.DescID): The parameter Id.
data (Any): the data, the user defines and we have to store.
flags (DESCFLAGS_SET): The input flags passed to define the operation.
Returns:
Union[Bool, tuple(bool, Any, DESCFLAGS_SET)]: The success status or the data to be returned.
"""
# Retrieves the world BaseContainer of this preference, where values have to be defined
bc = self.GetPreferenceContainer()
# Retrieves the parameter ID changed
paramID = id[0].id
# Store the values in the World Container
if paramID == c4d.PYPREFERENCE_STRING:
bc.SetString(WPREF_PYPREFERENCE_STRING, data)
return True, flags | c4d.DESCFLAGS_SET_PARAM_SET
if paramID == c4d.PYPREFERENCE_BUTTON:
return True, flags | c4d.DESCFLAGS_SET_PARAM_SET
return True
def GetDParameter(self, node, id, flags):
"""Called by Cinema 4D, when GetParameter is call from the node.
The main purpose is to return the data from the world container.
Args:
node (c4d.GeListNode): The instance of the PreferenceData.
id (c4d.DescID): The parameter Id.
flags (DESCFLAGS_GET): The input flags passed to define the operation.
Returns:
Union[Bool, tuple(bool, Any, DESCFLAGS_GET)]: The success status or the data to be returned.
"""
# Retrieves the world BaseContainer of this preference, where values have to be retrieved
bc = self.GetPreferenceContainer()
# Retrieves the parameter ID asked
paramID = id[0].id
# Returns the values from the World Container
if paramID == c4d.PYPREFERENCE_STRING:
return True, bc.GetString(WPREF_PYPREFERENCE_STRING), flags | c4d.DESCFLAGS_GET_PARAM_GET
# Instantiate a BitmapButtonStruct to be used by the Bitmap parameter
if paramID == c4d.PYPREFERENCE_BUTTON:
bbs = c4d.BitmapButtonStruct(node, id, 0)
return True, bbs, flags | c4d.DESCFLAGS_GET_PARAM_GET
return True
def Message(self, node, type, data):
# Determine the icon to use for the WPREF_PYPREFERENCE_BUTTON
if type == c4d.MSG_DESCRIPTION_GETBITMAP:
if data['id'][0].id == c4d.PYPREFERENCE_BUTTON:
iconOld = c4d.gui.GetIcon(1039689)
icon = c4d.IconData()
icon.bmp = iconOld['bmp']
icon.x = iconOld['x']
icon.y = iconOld['y']
icon.w = iconOld['w']
icon.h = iconOld['h']
icon.flags = c4d.ICONDATAFLAGS_NONE
data['bmp'] = icon.GetClonePart()
data['bmpflags'] = c4d.ICONDATAFLAGS_NONE
return True
# When a user click on the Button
if type == c4d.MSG_DESCRIPTION_COMMAND:
if data['id'][0].id == c4d.PYPREFERENCE_BUTTON:
path = c4d.storage.LoadDialog(c4d.FILESELECTTYPE_ANYTHING, "Select a file", c4d.FILESELECT_LOAD)
if path:
# Retrieves the world BaseContainer of this preference, where values have to be stored and save the path
bc = self.GetPreferenceContainer()
bc.SetString(WPREF_PYPREFERENCE_STRING, path)
return True
return True
if __name__ == '__main__':
c4d.plugins.RegisterPreferencePlugin(id=PLUGIN_ID,
g=Preference,
name="Py-Preference",
description="pypreference",
parentid=0,
sortid=0)
The pypreference.str
STRINGTABLE pypreference
{
pypreference "Py-Preference";
PYPREFERENCE_STRING "URL";
PYPREFERENCE_BUTTON "";
}
The pyprefrence.h
#ifndef PYPREFERENCE_H__
#define PYPREFERENCE_H__
enum
{
PYPREFERENCE_MAIN_GROUP = 999,
PYPREFERENCE_STRING = 1000,
PYPREFERENCE_BUTTON = 1001,
PYPREFERENCE_DUMMY
};
#endif // PYPREFERENCE_H__
The pyprefrence.res
CONTAINER pypreference
{
NAME pypreference;
GROUP PYPREFERENCE_MAIN_GROUP
{
DEFAULT 1;
COLUMNS 2;
STRING PYPREFERENCE_STRING { }
BITMAPBUTTON PYPREFERENCE_BUTTON { }
}
}
Just a side note previously, I don't remember exactly when it happened but the button was a text "..." and not a bitmap as it is right now.
Cheers,
Maxime.