Add functions to python via C++ SDK [R20]



  • In the past we added Coffee functions to allow the users to make some scripts using our plugins, with the deprecation of Coffee we cannot longer support them as we did with

    *Coffee cof = GetCoffeeMaster();
    cof->AddGlobalFunction("blablabla", (V_CODE)blablabla);

    Is there any chance to add custom functions to Python in C4d?

    Thank you in advance for any help.


  • Global Moderator

    Hi,

    You can add new functions with the C++ API using the Python library defined in frameworks/cinema.frameworksource/c4d_libs/lib_py.h
    This header is not documented but it allows to extend and embed Python.

    I already posted some code snippets and information in this topic.

    The posted code mostly compiles in R20 except the enumerations.

    Here's the code and corresponding files to add c4d.extendpyapi.HelloPython() Python function adapted for R20:

    extendpyapi.h

    #ifndef EXTENDPYAPI_H__
    #define EXTENDPYAPI_H__
    
    namespace extendpyapi
    {
    void InitExtendPython();
    void FreeExtendPython();
    }
    
    #endif
    

    extendpyapi.cpp

    #include "extendpyapi.h"
    
    #include "lib_py.h"
    
    namespace extendpyapi
    {
    
    // Global table of c4d.extendpyapi functions for PythonLibrary::InitModule()
    static maxon::BaseArray<PythonMethodData>* g_extendpyapi_functions = nullptr;
      
    // Prints "Hello from Python!" to the console
    static _PyObject *ExtendPyAPI_HelloPython(_PyObject *self)
    {
      PythonLibrary pylib;
      ApplicationOutput("Hello from Python!");
      return pylib.ReturnPyNone();
    }
    
    // Initializes c4d.extendpyapi module
    void InitExtendPyAPI()
    {
      PythonLibrary pylib;
    
      // Allocates c4d.extendpyapi module functions
      g_extendpyapi_functions = NewObjClear(maxon::BaseArray<PythonMethodData>);
      if (g_extendpyapi_functions == nullptr)
        return;
    
      // Initializes c4d.extendpyapi module functions
      g_extendpyapi_functions->Resize(2);
      PythonMethodData* moduleFunctions = g_extendpyapi_functions->GetFirst();
      moduleFunctions[0].Init("HelloPython", (PyFn)ExtendPyAPI_HelloPython, PYFN_FLAGS::NOARGS, "HelloPython() - Extend Python API");
      moduleFunctions[1].Init(String(), nullptr, PYFN_FLAGS::NONE, String()); // Last dummy element!
    
      // Initializes c4d.extendpyapi module
      if (pylib.InitModule("c4d.extendpyapi", moduleFunctions, "Extend Python API"))
        ApplicationOutput("\'c4d.extendpyapi\' module successfully initialized");
    }
    
    // Frees the module global function table
    void FreeExtendPyAPI()
    {
      DeleteObj(g_extendpyapi_functions);
    }
    
    }
    

    main.cpp

    #include "lib_py.h"
    
    #include "extendpyapi.h"
    
    ::Bool PluginStart()
    {
      return true;
    }
    
    void PluginEnd()
    {
    }
    
    ::Bool PluginMessage(::Int32 id, void* data)
    {
      switch (id)
      {
        case C4DPL_PYINITTYPES:
        {
          extendpyapi::InitExtendPyAPI();
          return true;
        }
    
        case C4DPL_PYFINALIZE:
        {
          extendpyapi::FreeExtendPyAPI();
          return true;
        }
    
        default: break;
      }
    
      return false;
    }
    

    Note ApplicationOutput() writes to the Default logger in the console and not the Python one.

    The use of the Python library defined in lib_py.h is not straightforward so do not hesitate to ask any question you may have.



  • How could I add a string parameter to that function? I mean to ExtendPyAPI_HelloPython,

    Regards, Víctor


  • Global Moderator

    Hi Víctor,

    To retrieve parameters a function must be initialized with PYFN_FLAGS::KEYWORDS. Then use pylib.ParseTupleAndKeywords() to get the value for each parameter.
    The following code shows the implementation of a function which expects a string, an integer and a float:

    static _PyObject *extendpyapi_PassParameters(_PyObject *self, _PyObject *args, _PyObject *keywords)
    {
    	PythonLibrary pylib;
    
    	String str;
    	Int32 integer = 0;
    	Float real = 0.0f;
    
    	const Char *kwlist[] = {"str", "integer", "real", nullptr};
    	if (!pylib.ParseTupleAndKeywords(args, keywords, "$if", kwlist, &str, &integer, &real))
    		return nullptr;
    
    	if (str.Content())
    		GePrint("Parameter str: " + str);
    
    	GePrint("Parameter integer: " + String::IntToString(integer));
    	GePrint("Parameter real: " + String::FloatToString(real));
    
    	return pylib.ReturnPyNone();
    }
    
    ...
    
    moduleFunctions[1].Init("PassParameters", (PyFn)extendpyapi_PassParameters, PYFN_FLAGS::KEYWORDS, "PassParameters() - Extend Python API");