SOLVED PreferenceData plugin, Text disappears when directory is set

Hello everyone,

I am having this problem that when I try to use a Filename parameter to get the path of a directory,

It either, in case I don't override the GetDParameter and SetDParameter functions does not save the path,
or, in case I do override these functions and set and get the plugin container myself, the path text in the UI disappears.

If someone can have a look I will greatly appreciate it. I created a self contained project illustrating the problem. Just unzip it in the plugins folder and it should work.

I am also going to go ahead and paste the python code here, in case some one can find something just by looking at the code, there is also of course accompanying resource files that are included in the zip file.

Thank you,
Alamgir Nasir

import c4d

PREFERENCE_PLUGIN_ID = 1040402
PREFERENCE_RENDER_PATH = 1000
def GetPreferenceContainer():
  world = c4d.GetWorldContainerInstance()
  if world is None:
    return None

  bc = world.GetContainerInstance(PREFERENCE_PLUGIN_ID)
  if bc is None:
    world.SetContainer(PREFERENCE_PLUGIN_ID, c4d.BaseContainer())
    bc = world.GetContainerInstance(PREFERENCE_PLUGIN_ID)
    if bc is None:
      return None

  return bc

class TestPreference(c4d.plugins.PreferenceData):

  def GetDParameter(self, node, id, flags):
    bc = GetPreferenceContainer()
    if bc is None:
      return False

    # Retrieves either check or number preference value
    paramID = id[0].id
    if paramID == PREFERENCE_RENDER_PATH:
      return (True, bc.GetFilename(PREFERENCE_RENDER_PATH), flags | c4d.DESCFLAGS_GET_PARAM_GET)
    return False

  def SetDParameter(self, node, id, data, flags):
    bc = GetPreferenceContainer()
    if bc is None:
      logger.error("SetDParameter: bc is none.")
      return False

    # Changes either check or number preference value
    paramID = id[0].id
    if paramID == PREFERENCE_RENDER_PATH:
      bc.SetFilename(PREFERENCE_RENDER_PATH, data)
      return (True, flags | c4d.DESCFLAGS_SET_PARAM_SET)
    return False

  def Register(self):
    print("Registered test preferences")
    return c4d.plugins.RegisterPreferencePlugin(
      id=PREFERENCE_PLUGIN_ID, g=TestPreference, name="TEST",
      description="testpreference", parentid=0, sortid=0)

TestPreference().Register()

Hi @potashalum, first of all, welcome in the plugincafe community!

Regarding the issue, as you may know, in Python there is no Filename object. So when you call

return (True, bc.GetFilename(PREFERENCE_RENDER_PATH), flags | c4d.DESCFLAGS_GET_PARAM_GET)

bc.GetFilename(PREFERENCE_RENDER_PATH) returns str, so it set a string as a parameter while the description expects a Filename.

To do so simply replace by GetCustomDataType (note it will print an error, but it's actually working, so you will need a try/except block. But I will investigate the error and fix it for a future release).

Then with that's said your GetPreferenceContainer is actually wrong. I replaced it with GetContainer.
Here the full code working

import c4d

PREFERENCE_PLUGIN_ID = 1040402
PREFERENCE_RENDER_PATH = 1000

def GetContainer(node=None):
  bc = None
  if node is None:
    plug = c4d.plugins.FindPlugin(PREFERENCE_PLUGIN_ID, c4d.PLUGINTYPE_PREFS)
    if plug is None:
        return
    bc = plug.GetDataInstance()
  
  else:
    bc = node.GetDataInstance()

  return bc

class TestPreference(c4d.plugins.PreferenceData):

  def GetDParameter(self, node, id, flags):
    bc = GetContainer(node)
    if bc is None:
      return False

    # Retrieves either check or number preference value
    paramID = id[0].id
    if paramID == PREFERENCE_RENDER_PATH:
      try:
        return (True, bc.GetCustomDataType(PREFERENCE_RENDER_PATH), flags | c4d.DESCFLAGS_GET_PARAM_GET)
      except:
        return False
    return False

  def SetDParameter(self, node, id, data, flags):
    bc = GetContainer()
    if bc is None:
      print ("SetDParameter: bc is none.")
      return False

    # Changes either check or number preference value
    paramID = id[0].id
    if paramID == PREFERENCE_RENDER_PATH:
      bc.SetFilename(PREFERENCE_RENDER_PATH, data)
      return (True, flags | c4d.DESCFLAGS_SET_PARAM_SET)
    return False

  def Register(self):
    print("Registered test preferences")
    return c4d.plugins.RegisterPreferencePlugin(
      id=PREFERENCE_PLUGIN_ID, g=TestPreference, name="TEST",
      description="testpreference", parentid=0, sortid=0)

TestPreference().Register()

I will add a note in the documentation about Filename.

If you have any question, please let me know.
Cheers,
Maxime.

Thank you for your help @m_adam, but I am still having the same problem.

Using GetCustomDataType doesn't give me any errors but it doesn't work either. It still clears the string immediately after I set the field.

It does seem like the problem is still with the GetDParameter, because if I comment it out everything works except that the filename isn't loaded when cinema is restarted.

Hi @potashalum did you update your GetPreferenceContainer function as well?

By copy/pasting the code provided you also get the issue?
Cheers,
Maxime.

Hi @m_adam,

I updated the GetPreferenceContainer function as well, it did help in that the SetDParameter function works now, which didn't before. So that is definitely better. But the GetDParameter function didn't work, even if I only use your code.

Could it be a problem in the res files? Would you mind taking a look please. I tried my best to get those right but maybe I still have an error there.

Regards,
Alamgir

Hi @potashalum, I'm terribly sorry I missed your reply when I came back from holiday.

After digging more into this, it's currently not possible to handle Filename from python in GetDParameter of a world preference.
A workaround would be to store parameter as a string, display a string instead of a FileName and make a button to define this string parameter.

Cheers,
Maxime.

Hi @m_adam,

No problem, I hope you had great holidays and thank you for confirming the problem.

Regards,
Alamgir