Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    How to expose an object function done in C++

    SDK Help
    0
    19
    3.5k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 15/07/2016 at 07:13, xxxxxxxx wrote:

      Hi!

      Sorry for the late reply. It's possible to define new Python functions using the library defined in c4d_libs/lib_py.h
      This library definitions are not documented currenly and many are meant for internal use only.

      To expose a Python function a new module has to be initialized with PythonLibrary::InitModule().
      Here is some code:

      // 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;
        GePrint("Hello from Python!");
        return pylib.ReturnPyNone();
      }
        
      // Initializes c4d.extendpyapi module
      void InitExtendPython()
      {
        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_0, String()); // Last dummy element!
        
        // Initializes c4d.extendpyapi module
        if (pylib.InitModule("c4d.extendpyapi", moduleFunctions, "Extend Python API"))
          GePrint("\'c4d.extendpyapi\' module successfully initialized");
      }
       
      // Frees the module global function table
      void FreeExtendPython()
      {
        DeleteObj(g_extendpyapi_functions);
      }
      

      InitExtendPython()/FreeExtendPython() have to called from within PluginMessage() C4DPL_PYINITTYPES/C4DPL_PYFINALIZE:

      Bool PluginMessage(Int32 id, void* data)
      {
        case C4DPL_PYINITTYPES:
        {
          InitExtendPython();
          return true;
        }
        case C4DPL_PYFINALIZE:
        {
          FreeExtendPython();
          return true;
        }
        
        return false;
      }
      

      To pass basic parameters like string, integer or float use PythonLibrary::ParseTupleAndKeywords() like shown in the following code snippet:

      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();
      }
      

      If a function accepts parameters pass PYFN_FLAGS_KEYWORDS instead of PYFN_FLAGS_NOARGS for the Init() call.

      Finally, to parse a GeData/baselist object use 'G' format:

      static _PyObject *extendpyapi_PassBaseList(_PyObject *self, _PyObject *args, _PyObject *keywords)
      {
        PythonLibrary pylib;
       
        GeData data;
       
        const Char *kwlist[] = {"baselist", nullptr};
        if (!pylib.ParseTupleAndKeywords(args, keywords, "G", kwlist, &data))
          return nullptr;
       
        if (data.GetType() == DA_ALIASLINK)
        {
          BaseLink* link = data.GetBaseLink();
          if (link == nullptr)
            return pylib.ReturnPyNone();
       
          BaseList2D* baseList = link->GetLink(nullptr);
          if (baseList == nullptr)
            return pylib.ReturnPyNone();
       
          GePrint("Passed baselist: " + baseList->GetName());
        }
       
        return pylib.ReturnPyNone();
      }
      
      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 15/07/2016 at 10:15, xxxxxxxx wrote:

        Howdy,

        Wow, thanks Yannick!

        I've been working on making aspects of my plugins scriptable through cofffee and wanted to included python scriptability, too. This gives me a place to start. 😉

        Adios,
        Cactus Dan

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 28/07/2016 at 16:15, xxxxxxxx wrote:

          Howdy,

          OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?

          Adios,
          Cactus Dan

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 29/07/2016 at 02:37, xxxxxxxx wrote:

            Originally posted by xxxxxxxx

            OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?

            Like the argument parsing in the function example extendpyapi_PassParameters() I posted above, concatenate the characters for each parameter.
            For instance:

            GeData data;
            Int32 integer = 0;
            Float real = 0.0f;
              
            const Char *kwlist[] = {"baselist", "integer", "real", nullptr};
            if (!pylib.ParseTupleAndKeywords(args, keywords, "Gif", kwlist, &data, &integer, &real))
                return nullptr;
            
            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 29/07/2016 at 04:53, xxxxxxxx wrote:

              Howdy,

              DOH! It's the strings that always throw me off when they're used for anything other than printing.
              But I get it now:
              "$" = string format
              "i" = integer format
              "f" = float format
              "G" = GeData format

              Possibly if the example had the float first like "$fi" then I might have been able to figure that out. But, with it the other way around, no matter how many times I looked at it, I still saw the word "if", and got confused. 😊

              Thanks for the clarification.

              Adios,
              Cactus Dan

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 29/07/2016 at 05:53, xxxxxxxx wrote:

                You're welcome 🙂

                "C" can also be used to parse BaseContainer* too (note only pointer to BaseContainer, initialize with nullptr before calling ParseTupleAndKeywords()).

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 29/07/2016 at 06:07, xxxxxxxx wrote:

                  Howdy,

                  Thanks. Is there a complete list of formatting letters that you could post?

                  Also, is there a way to add global symbols in python like in coffee, and if so, can you post an example?

                  Adios,
                  Cactus Dan

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 26/04/2017 at 00:05, xxxxxxxx wrote:

                    Hello,

                    I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.

                    So again, we just need a table of all formatting letters and types that Cinema python binding API uses.

                    Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 26/04/2017 at 02:38, xxxxxxxx wrote:

                      Hi Miro,

                      Originally posted by xxxxxxxx

                      I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.

                      To parse a BaseList based object, use 'G' format character as show in my above code snippet's function  extendpyapi_PassBaseList(). The object can be casted to the most interesting type after retrieving it.

                      Originally posted by xxxxxxxx

                      So again, we just need a table of all formatting letters and types that Cinema python binding API uses.

                      PythonLibrary::ParseTupleAndKeywords() accepts the following format characters for Cinema 4D C++ API classes:
                      - $: String
                      - %: Filename
                      - M: Matrix
                      - V: Vector
                      - Q: Quaternion
                      - 😄 BaseContainer*
                      - G: GeData (versatile: can parse a BaseList, a custom data, a time, etc.)
                      - T: BaseTime
                      - X: BaseThread

                      Originally posted by xxxxxxxx

                      Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords

                      PythonLibrary::ParseTupleAndKeywords()also accepts the following standard format characters: b, B, j, h, i, I, v, l, L, r, f, d, c

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 27/04/2017 at 23:34, xxxxxxxx wrote:

                        That's perfect! Thank you.

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 24/09/2017 at 13:07, xxxxxxxx wrote:

                          Hello all,

                          It seems to be that i's only possible to make a python extension library as a plugin. Which plugin then is best suited? A command plugin, I would guess, or is there another plugin that loads completely automatic at startup?

                          I need something that is always available, so that I don't have to think about loading the library. Much like the c++ example here: page_creating_libraries.html

                          Regards,

                          Hermen

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            On 25/09/2017 at 01:29, xxxxxxxx wrote:

                            Hello,

                            a "_ plugin_" is just a custom module loaded by Cinema. So a "plugin" does not need to include anything e.g. a command data extension. You only have to implement PluginStart()/PluginMessage()/PluginEnd(), see Plugin Functions Manual. In such a PluginMessage () function you have to register your Python extension as shown above.

                            best wishes,
                            Sebastian

                            1 Reply Last reply Reply Quote 0
                            • H
                              Helper
                              last edited by

                              On 25/09/2017 at 02:53, xxxxxxxx wrote:

                              Aha!
                              Now I understand, thanks for clearing that up!

                              regards,

                              Hermen

                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post